diff options
52 files changed, 773 insertions, 261 deletions
diff --git a/doc/release-process.md b/doc/release-process.md index 5b96e53753..affe6ed13a 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -50,8 +50,7 @@ Release Process wget 'ftp://ftp.simplesystems.org/pub/png/src/history/libpng16/libpng-1.6.8.tar.gz' wget 'https://fukuchi.org/works/qrencode/qrencode-3.4.3.tar.bz2' wget 'https://downloads.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.tar.bz2' - wget 'https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch' -O \ - boost-mingw-gas-cross-compile-2013-03-03.patch + wget 'https://svn.boost.org/trac/boost/raw-attachment/ticket/7262/boost-mingw.patch' -O boost-mingw-gas-cross-compile-2013-03-03.patch wget 'https://download.qt-project.org/official_releases/qt/5.2/5.2.0/single/qt-everywhere-opensource-src-5.2.0.tar.gz' wget 'https://download.qt-project.org/archive/qt/4.6/qt-everywhere-opensource-src-4.6.4.tar.gz' wget 'https://protobuf.googlecode.com/files/protobuf-2.5.0.tar.bz2' @@ -61,10 +60,8 @@ Release Process wget 'http://www.opensource.apple.com/tarballs/ld64/ld64-127.2.tar.gz' wget 'http://cdrkit.org/releases/cdrkit-1.1.11.tar.gz' wget 'https://github.com/theuni/libdmg-hfsplus/archive/libdmg-hfsplus-v0.1.tar.gz' - wget 'http://llvm.org/releases/3.2/clang+llvm-3.2-x86-linux-ubuntu-12.04.tar.gz' -O \ - clang-llvm-3.2-x86-linux-ubuntu-12.04.tar.gz - wget 'https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff' -O \ - cdrkit-deterministic.patch + wget 'http://llvm.org/releases/3.2/clang+llvm-3.2-x86-linux-ubuntu-12.04.tar.gz' -O clang-llvm-3.2-x86-linux-ubuntu-12.04.tar.gz + wget 'https://raw.githubusercontent.com/theuni/osx-cross-depends/master/patches/cdrtools/genisoimage.diff' -O cdrkit-deterministic.patch cd .. ./bin/gbuild ../bitcoin/contrib/gitian-descriptors/boost-linux.yml mv build/out/boost-*.zip inputs/ @@ -180,34 +177,6 @@ Commit your signature to gitian.sigs: ### After 3 or more people have gitian-built, repackage gitian-signed zips: -From a directory containing bitcoin source, gitian.sigs and gitian zips - - export VERSION=(new version, e.g. 0.8.0) - mkdir bitcoin-${VERSION}-linux-gitian - pushd bitcoin-${VERSION}-linux-gitian - unzip ../bitcoin-${VERSION}-linux-gitian.zip - mkdir gitian - cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ - for signer in $(ls ../gitian.sigs/${VERSION}/); do - cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert - cp ../gitian.sigs/${VERSION}/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig - done - zip -r bitcoin-${VERSION}-linux-gitian.zip * - cp bitcoin-${VERSION}-linux-gitian.zip ../ - popd - mkdir bitcoin-${VERSION}-win-gitian - pushd bitcoin-${VERSION}-win-gitian - unzip ../bitcoin-${VERSION}-win-gitian.zip - mkdir gitian - cp ../bitcoin/contrib/gitian-downloader/*.pgp ./gitian/ - for signer in $(ls ../gitian.sigs/${VERSION}-win/); do - cp ../gitian.sigs/${VERSION}-win/${signer}/bitcoin-build.assert ./gitian/${signer}-build.assert - cp ../gitian.sigs/${VERSION}-win/${signer}/bitcoin-build.assert.sig ./gitian/${signer}-build.assert.sig - done - zip -r bitcoin-${VERSION}-win-gitian.zip * - cp bitcoin-${VERSION}-win-gitian.zip ../ - popd - - Upload gitian zips to SourceForge - Announce the release: diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py new file mode 100755 index 0000000000..7f2d79b3c1 --- /dev/null +++ b/qa/rpc-tests/receivedby.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Exercise the listtransactions API + +# Add python-bitcoinrpc to module search path: + +import os +import sys +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "python-bitcoinrpc")) + +import json +import shutil +import subprocess +import tempfile +import traceback + +from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException +from util import * + +def get_sub_array_from_array(object_array, to_match): + ''' + Finds and returns a sub array from an array of arrays. + to_match should be a unique idetifier of a sub array + ''' + num_matched = 0 + for item in object_array: + all_match = True + for key,value in to_match.items(): + if item[key] != value: + all_match = False + if not all_match: + continue + return item + return [] + +def check_array_result(object_array, to_match, expected, should_not_find = False): + """ + Pass in array of JSON objects, a dictionary with key/value pairs + to match against, and another dictionary with expected key/value + pairs. + If the should_not_find flag is true, to_match should not be found in object_array + """ + if should_not_find == True: + expected = { } + num_matched = 0 + for item in object_array: + all_match = True + for key,value in to_match.items(): + if item[key] != value: + all_match = False + if not all_match: + continue + for key,value in expected.items(): + if item[key] != value: + raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value))) + num_matched = num_matched+1 + if num_matched == 0 and should_not_find != True: + raise AssertionError("No objects matched %s"%(str(to_match))) + if num_matched > 0 and should_not_find == True: + raise AssertionError("Objects was matched %s"%(str(to_match))) + +def run_test(nodes): + ''' + listreceivedbyaddress Test + ''' + # Send from node 0 to 1 + addr = nodes[1].getnewaddress() + txid = nodes[0].sendtoaddress(addr, 0.1) + sync_mempools(nodes) + + #Check not listed in listreceivedbyaddress because has 0 confirmations + check_array_result(nodes[1].listreceivedbyaddress(), + {"address":addr}, + { }, + True) + #Bury Tx under 10 block so it will be returned by listreceivedbyaddress + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + check_array_result(nodes[1].listreceivedbyaddress(), + {"address":addr}, + {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) + #With min confidence < 10 + check_array_result(nodes[1].listreceivedbyaddress(5), + {"address":addr}, + {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) + #With min confidence > 10, should not find Tx + check_array_result(nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True) + + #Empty Tx + addr = nodes[1].getnewaddress() + check_array_result(nodes[1].listreceivedbyaddress(0,True), + {"address":addr}, + {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]}) + + ''' + getreceivedbyaddress Test + ''' + # Send from node 0 to 1 + addr = nodes[1].getnewaddress() + txid = nodes[0].sendtoaddress(addr, 0.1) + sync_mempools(nodes) + + #Check balance is 0 because of 0 confirmations + balance = nodes[1].getreceivedbyaddress(addr) + if balance != Decimal("0.0"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + #Check balance is 0.1 + balance = nodes[1].getreceivedbyaddress(addr,0) + if balance != Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + balance = nodes[1].getreceivedbyaddress(addr) + if balance != Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) + + ''' + listreceivedbyaccount + getreceivedbyaccount Test + ''' + #set pre-state + addrArr = nodes[1].getnewaddress() + account = nodes[1].getaccount(addrArr) + received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(),{"account":account}) + if len(received_by_account_json) == 0: + raise AssertionError("No accounts found in node") + balance_by_account = rec_by_accountArr = nodes[1].getreceivedbyaccount(account) + + txid = nodes[0].sendtoaddress(addr, 0.1) + + # listreceivedbyaccount should return received_by_account_json because of 0 confirmations + check_array_result(nodes[1].listreceivedbyaccount(), + {"account":account}, + received_by_account_json) + + # getreceivedbyaddress should return same balance because of 0 confirmations + balance = nodes[1].getreceivedbyaccount(account) + if balance != balance_by_account: + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + + nodes[1].setgenerate(True, 10) + sync_blocks(nodes) + # listreceivedbyaccount should return updated account balance + check_array_result(nodes[1].listreceivedbyaccount(), + {"account":account}, + {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))}) + + # getreceivedbyaddress should return updates balance + balance = nodes[1].getreceivedbyaccount(account) + if balance != balance_by_account + Decimal("0.1"): + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + + #Create a new account named "mynewaccount" that has a 0 balance + nodes[1].getaccountaddress("mynewaccount") + received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"}) + if len(received_by_account_json) == 0: + raise AssertionError("No accounts found in node") + + # Test includeempty of listreceivedbyaccount + if received_by_account_json["amount"] != Decimal("0.0"): + raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"])) + + # Test getreceivedbyaccount for 0 amount accounts + balance = nodes[1].getreceivedbyaccount("mynewaccount") + if balance != Decimal("0.0"): + raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) + +def main(): + import optparse + + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", + help="Leave bitcoinds and test.* datadir on exit or error") + parser.add_option("--srcdir", dest="srcdir", default="../../src", + help="Source directory containing bitcoind/bitcoin-cli (default: %default%)") + parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), + help="Root directory for datadirs") + (options, args) = parser.parse_args() + + os.environ['PATH'] = options.srcdir+":"+os.environ['PATH'] + + check_json_precision() + + success = False + nodes = [] + try: + print("Initializing test directory "+options.tmpdir) + if not os.path.isdir(options.tmpdir): + os.makedirs(options.tmpdir) + initialize_chain(options.tmpdir) + + nodes = start_nodes(2, options.tmpdir) + connect_nodes(nodes[1], 0) + sync_blocks(nodes) + + run_test(nodes) + + success = True + + except AssertionError as e: + print("Assertion failed: "+e.message) + except Exception as e: + print("Unexpected exception caught during testing: "+str(e)) + traceback.print_tb(sys.exc_info()[2]) + + if not options.nocleanup: + print("Cleaning up") + stop_nodes(nodes) + wait_bitcoinds() + shutil.rmtree(options.tmpdir) + + if success: + print("Tests successful") + sys.exit(0) + else: + print("Failed") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py index eded098c7c..27c9f778f6 100644 --- a/qa/rpc-tests/util.py +++ b/qa/rpc-tests/util.py @@ -182,6 +182,10 @@ def wait_bitcoinds(): def connect_nodes(from_connection, node_num): ip_port = "127.0.0.1:"+str(p2p_port(node_num)) from_connection.addnode(ip_port, "onetry") + # poll until version handshake complete to avoid race conditions + # with transaction relaying + while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()): + time.sleep(0.1) def find_output(node, txid, amount): """ diff --git a/qa/rpc-tests/util.sh b/qa/rpc-tests/util.sh index 1e7bd6a7ee..b726ef627f 100644 --- a/qa/rpc-tests/util.sh +++ b/qa/rpc-tests/util.sh @@ -38,6 +38,10 @@ function AssertEqual { if (( $( echo "$1 == $2" | bc ) == 0 )) then echoerr "AssertEqual: $1 != $2" + declare -f CleanUp > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + CleanUp + fi exit 1 fi } @@ -49,6 +53,10 @@ function CheckBalance { if (( $( echo "$B == $EXPECT" | bc ) == 0 )) then echoerr "bad balance: $B (expected $2)" + declare -f CleanUp > /dev/null 2>&1 + if [[ $? -eq 0 ]] ; then + CleanUp + fi exit 1 fi } diff --git a/qa/rpc-tests/zapwallettxes.sh b/qa/rpc-tests/zapwallettxes.sh new file mode 100755 index 0000000000..bc52a7dacd --- /dev/null +++ b/qa/rpc-tests/zapwallettxes.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Test -zapwallettxes=<mode> + +if [ $# -lt 1 ]; then + echo "Usage: $0 path_to_binaries" + echo "e.g. $0 ../../src" + exit 1 +fi + +set -f + +BITCOIND=${1}/bitcoind +CLI=${1}/bitcoin-cli + +DIR="${BASH_SOURCE%/*}" +SENDANDWAIT="${DIR}/send.sh" +if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi +. "$DIR/util.sh" + +D=$(mktemp -d test.XXXXX) + +D1=${D}/node1 +CreateDataDir "$D1" port=11000 rpcport=11001 +B1ARGS="-datadir=$D1" +$BITCOIND $B1ARGS & +B1PID=$! + +D2=${D}/node2 +CreateDataDir "$D2" port=11010 rpcport=11011 +B2ARGS="-datadir=$D2" +$BITCOIND $B2ARGS & +B2PID=$! + +function CleanUp { +$CLI $B2ARGS stop > /dev/null 2>&1 +wait $B2PID +$CLI $B1ARGS stop > /dev/null 2>&1 +wait $B1PID + +rm -rf $D +} + +# 110 blocks, 10 mature == 500 XBT +$CLI $B1ARGS setgenerate true 110 +$CLI $B2ARGS setgenerate true 110 + +CheckBalance "$B1ARGS" 500 +CheckBalance "$B2ARGS" 500 + +# Send 10 XBT +TXID1_DEFAULT=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10) +TXID2_DEFAULT=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 10) + +CheckBalance $B1ARGS 490 +CheckBalance $B2ARGS 490 + +# Move 10 XBT to testaccount +TMP=$($CLI $B1ARGS move "" "testaccount" 10) +TMP=$($CLI $B2ARGS move "" "testaccount" 10) + +CheckBalance $B1ARGS 10 "testaccount" +CheckBalance $B2ARGS 10 "testaccount" + +# Send 1 XBT from testaccount +TXID1_TESTACCOUNT=$($CLI $B1ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) +TXID2_TESTACCOUNT=$($CLI $B2ARGS sendfrom "testaccount" "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) + +CheckBalance $B1ARGS 9 "testaccount" +CheckBalance $B2ARGS 9 "testaccount" + +CheckBalance $B1ARGS 489 +CheckBalance $B2ARGS 489 + +# Confirm transactions +$CLI $B1ARGS setgenerate true 1 +$CLI $B2ARGS setgenerate true 1 + +# Create unconfirmed transaction +TXID1_UNCONFIRMED=$($CLI $B1ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) +TXID2_UNCONFIRMED=$($CLI $B2ARGS sendtoaddress "mrhz5ZgSF3C1BSdyCKt3gEdhKoRL5BNfJV" 1) + +# check balance (we created another 50 and spent 1 in the meantime) +CheckBalance $B1ARGS 538 +CheckBalance $B2ARGS 538 + +# Safety check, if unconfirmed transactions are there +$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "gettransaction1_1: $TXID1_UNCONFIRMED failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "gettransaction2_1: $TXID2_UNCONFIRMED failed" + CleanUp + exit 1 +fi + +# stop nodes +$CLI $B2ARGS stop > /dev/null 2>&1 +wait $B2PID +$CLI $B1ARGS stop > /dev/null 2>&1 +wait $B1PID + +# restart nodes with -zapwallettxes +$BITCOIND -zapwallettxes=1 $B1ARGS & +B1PID=$! +$BITCOIND -zapwallettxes=2 $B2ARGS & +B2PID=$! + +# check if confirmed transactions are there +$CLI $B1ARGS gettransaction $TXID1_DEFAULT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 1: $TXID1_DEFAULT failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_DEFAULT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 2: $TXID2_DEFAULT failed" + CleanUp + exit 1 +fi +$CLI $B1ARGS gettransaction $TXID1_TESTACCOUNT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 3: $TXID1_TESTACCOUNT failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_TESTACCOUNT > /dev/null 2>&1 +if [[ $? -ne 0 ]] ; then + echoerr "check confirmed transaction 4: $TXID2_TESTACCOUNT failed" + CleanUp + exit 1 +fi + +# check if unconfirmed transaction is gone +$CLI $B1ARGS gettransaction $TXID1_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -eq 0 ]] ; then + echoerr "check unconfirmed transaction 1: $TXID1_UNCONFIRMED failed" + CleanUp + exit 1 +fi +$CLI $B2ARGS gettransaction $TXID2_UNCONFIRMED > /dev/null 2>&1 +if [[ $? -eq 0 ]] ; then + echoerr "check unconfirmed transaction 2: $TXID2_UNCONFIRMED failed" + CleanUp + exit 1 +fi + +# check zapwallet mode 1, testaccount balance must be 9 (keeping transaction metadata) +CheckBalance $B1ARGS 9 "testaccount" + +# check zapwallet mode 2, testaccount balance must be 10 (dropping transaction metadata) +CheckBalance $B2ARGS 10 "testaccount" + +echo "Tests successful, cleaning up" +CleanUp +exit 0 diff --git a/src/Makefile.am b/src/Makefile.am index 3948ca0780..5ecded442c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ $(LIBLEVELDB) $(LIBMEMENV): endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config -BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BITCOIN_CONFIG_INCLUDES) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) +BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) noinst_LIBRARIES = \ libbitcoin_server.a \ diff --git a/src/bloom.cpp b/src/bloom.cpp index 1bfcbd406f..26e366179c 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -99,7 +99,7 @@ bool CBloomFilter::IsWithinSizeConstraints() const return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; } -bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash) +bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) { bool fFound = false; // Match if the filter contains the hash of tx @@ -108,6 +108,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& ha return true; if (isEmpty) return false; + const uint256& hash = tx.GetHash(); if (contains(hash)) fFound = true; diff --git a/src/bloom.h b/src/bloom.h index 75e3f38c55..956bead87f 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -83,7 +83,7 @@ public: bool IsWithinSizeConstraints() const; // Also adds any outputs which match the filter to the filter (to match their spending txes) - bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash); + bool IsRelevantAndUpdate(const CTransaction& tx); // Checks for empty and full filters to avoid wasting cpu void UpdateEmptyFull(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 31eac62d48..afbae6fc57 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -127,7 +127,7 @@ public: // CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) // vMerkleTree: 4a5e1e const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; - CTransaction txNew; + 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)); diff --git a/src/clientversion.h b/src/clientversion.h index 29b4aa3764..a30bbff06b 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -2,7 +2,7 @@ #define CLIENTVERSION_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #else // // client versioning and copyright year diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index bb870c01f8..22f82e4259 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <cstddef> diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index 6e5bae8a48..d93602e0fe 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <cstddef> diff --git a/src/core.cpp b/src/core.cpp index 6039986e6c..6c5ee1c0f8 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -91,11 +91,34 @@ std::string CFeeRate::ToString() const return result; } -uint256 CTransaction::GetHash() const +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(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; +} + int64_t CTransaction::GetValueOut() const { int64_t nValueOut = 0; diff --git a/src/core.h b/src/core.h index 0e59129349..27fda95552 100644 --- a/src/core.h +++ b/src/core.h @@ -203,49 +203,59 @@ public: }; +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 CFeeRate minTxFee; static CFeeRate minRelayTxFee; static const int CURRENT_VERSION=1; - int nVersion; - std::vector<CTxIn> vin; - std::vector<CTxOut> vout; - unsigned int nLockTime; - CTransaction() - { - SetNull(); - } + // 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 int nVersion; + const std::vector<CTxIn> vin; + const std::vector<CTxOut> vout; + const unsigned int nLockTime; - IMPLEMENT_SERIALIZE - ( - READWRITE(this->nVersion); + /** Construct a CTransaction that qualifies as IsNull() */ + CTransaction(); + + /** Convert a CMutableTransaction into a CTransaction. */ + CTransaction(const CMutableTransaction &tx); + + CTransaction& operator=(const CTransaction& tx); + + IMPLEMENT_SERIALIZE( + READWRITE(*const_cast<int*>(&this->nVersion)); nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); + READWRITE(*const_cast<std::vector<CTxIn>*>(&vin)); + READWRITE(*const_cast<std::vector<CTxOut>*>(&vout)); + READWRITE(*const_cast<unsigned int*>(&nLockTime)); + if (fRead) + UpdateHash(); ) - void SetNull() - { - nVersion = CTransaction::CURRENT_VERSION; - vin.clear(); - vout.clear(); - nLockTime = 0; + bool IsNull() const { + return vin.empty() && vout.empty(); } - bool IsNull() const - { - return (vin.empty() && vout.empty()); + const uint256& GetHash() const { + return hash; } - uint256 GetHash() const; - // Return sum of txouts. int64_t GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because @@ -261,22 +271,43 @@ public: friend bool operator==(const CTransaction& a, const CTransaction& b) { - return (a.nVersion == b.nVersion && - a.vin == b.vin && - a.vout == b.vout && - a.nLockTime == b.nLockTime); + return a.hash == b.hash; } friend bool operator!=(const CTransaction& a, const CTransaction& b) { - return !(a == b); + return a.hash != b.hash; } - std::string ToString() const; void print() const; }; +/** A mutable version of CTransaction. */ +struct CMutableTransaction +{ + int nVersion; + std::vector<CTxIn> vin; + std::vector<CTxOut> vout; + unsigned int nLockTime; + + CMutableTransaction(); + CMutableTransaction(const CTransaction& tx); + + IMPLEMENT_SERIALIZE( + 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; +}; + /** wrapper for CTxOut that provides a more compact serialization */ class CTxOutCompressor { @@ -465,12 +496,6 @@ public: uint256 BuildMerkleTree() const; - const uint256 &GetTxHash(unsigned int nIndex) const { - assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first - assert(nIndex < vtx.size()); - return vMerkleTree[nIndex]; - } - std::vector<uint256> GetMerkleBranch(int nIndex) const; static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex); void print() const; diff --git a/src/init.cpp b/src/init.cpp index 12d2d1bb44..7ef3bc5dac 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "init.h" @@ -260,7 +260,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n"; strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + _("(default: wallet.dat)") + "\n"; strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -zapwallettxes " + _("Clear list of wallet transactions (diagnostic tool; implies -rescan)") + "\n"; + strUsage += " -zapwallettxes=<mode> " + _("Delete all wallet transactions and only recover those part of the blockchain through -rescan on startup") + "\n"; + strUsage += " " + _("(default: 1, 1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)") + "\n"; #endif strUsage += "\n" + _("Debugging/Testing options:") + "\n"; @@ -536,7 +537,7 @@ bool AppInit2(boost::thread_group& threadGroup) // -zapwallettx implies a rescan if (GetBoolArg("-zapwallettxes", false)) { if (SoftSetBoolArg("-rescan", true)) - LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=1 -> setting -rescan=1\n"); + LogPrintf("AppInit2 : parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n"); } // Make sure enough file descriptors are available @@ -993,11 +994,15 @@ bool AppInit2(boost::thread_group& threadGroup) pwalletMain = NULL; LogPrintf("Wallet disabled!\n"); } else { + + // needed to restore wallet transaction meta data after -zapwallettxes + std::vector<CWalletTx> vWtx; + if (GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); pwalletMain = new CWallet(strWalletFile); - DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); return false; @@ -1092,6 +1097,29 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; + + // Restore wallet transaction metadata after -zapwallettxes=1 + if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") + { + BOOST_FOREACH(const CWalletTx& wtxOld, vWtx) + { + uint256 hash = wtxOld.GetHash(); + std::map<uint256, CWalletTx>::iterator mi = pwalletMain->mapWallet.find(hash); + if (mi != pwalletMain->mapWallet.end()) + { + const CWalletTx* copyFrom = &wtxOld; + CWalletTx* copyTo = &mi->second; + copyTo->mapValue = copyFrom->mapValue; + copyTo->vOrderForm = copyFrom->vOrderForm; + copyTo->nTimeReceived = copyFrom->nTimeReceived; + copyTo->nTimeSmart = copyFrom->nTimeSmart; + copyTo->fFromMe = copyFrom->fFromMe; + copyTo->strFromAccount = copyFrom->strFromAccount; + copyTo->nOrderPos = copyFrom->nOrderPos; + copyTo->WriteToDisk(); + } + } + } } } // (!fDisableWallet) #else // ENABLE_WALLET diff --git a/src/main.cpp b/src/main.cpp index 006ec7cabc..d3f04b95fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -130,8 +130,8 @@ namespace { namespace { struct CMainSignals { - // Notifies listeners of updated transaction data (passing hash, transaction, and optionally the block it is found in. - boost::signals2::signal<void (const uint256 &, const CTransaction &, const CBlock *)> SyncTransaction; + // 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). @@ -146,7 +146,7 @@ struct CMainSignals { } void RegisterWallet(CWalletInterface* pwalletIn) { - g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); @@ -160,7 +160,7 @@ void UnregisterWallet(CWalletInterface* pwalletIn) { g_signals.SetBestChain.disconnect(boost::bind(&CWalletInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CWalletInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.EraseTransaction.disconnect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2, _3)); + g_signals.SyncTransaction.disconnect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); } void UnregisterAllWallets() { @@ -172,8 +172,8 @@ void UnregisterAllWallets() { g_signals.SyncTransaction.disconnect_all_slots(); } -void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) { - g_signals.SyncTransaction(hash, tx, pblock); +void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) { + g_signals.SyncTransaction(tx, pblock); } ////////////////////////////////////////////////////////////////////////////// @@ -952,7 +952,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa pool.addUnchecked(hash, entry); } - g_signals.SyncTransaction(hash, tx, NULL); + g_signals.SyncTransaction(tx, NULL); return true; } @@ -1479,7 +1479,7 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) { bool ret; // mark inputs spent @@ -1494,7 +1494,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach } // add outputs - ret = inputs.SetCoins(txhash, CCoins(tx, nHeight)); + ret = inputs.SetCoins(tx.GetHash(), CCoins(tx, nHeight)); assert(ret); } @@ -1767,8 +1767,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { - for (unsigned int i = 0; i < block.vtx.size(); i++) { - uint256 hash = block.GetTxHash(i); + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + const uint256& hash = tx.GetHash(); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); @@ -1829,11 +1829,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C } CTxUndo txundo; - UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); - vPos.push_back(std::make_pair(block.GetTxHash(i), pos)); + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime = GetTimeMicros() - nStart; @@ -1892,13 +1892,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C assert(ret); // Watch for transactions paying to me - for (unsigned int i = 0; i < block.vtx.size(); i++) - g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); + BOOST_FOREACH(const CTransaction& tx, block.vtx) + g_signals.SyncTransaction(tx, &block); // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; g_signals.UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = block.GetTxHash(0); + hashPrevBestCoinBase = block.vtx[0].GetHash(); return true; } @@ -1996,7 +1996,7 @@ bool static DisconnectTip(CValidationState &state) { // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx.GetHash(), tx, NULL); + SyncWithWallets(tx, NULL); } return true; } @@ -2036,11 +2036,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx.GetHash(), tx, NULL); + SyncWithWallets(tx, NULL); } // ... and about transactions that got confirmed: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx.GetHash(), tx, &block); + SyncWithWallets(tx, &block); } return true; } @@ -2381,16 +2381,11 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); - // Build the merkle tree already. We need it anyway later, and it makes the - // block cache the transaction hashes, which means they don't need to be - // recalculated many times during this block's validation. - block.BuildMerkleTree(); - // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set<uint256> uniqueTx; - for (unsigned int i = 0; i < block.vtx.size(); i++) { - uniqueTx.insert(block.GetTxHash(i)); + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + uniqueTx.insert(tx.GetHash()); } if (uniqueTx.size() != block.vtx.size()) return state.DoS(100, error("CheckBlock() : duplicate transaction"), @@ -2406,7 +2401,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo REJECT_INVALID, "bad-blk-sigops", true); // Check merkle root - if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back()) + if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree()) return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); @@ -2682,8 +2677,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) for (unsigned int i = 0; i < block.vtx.size(); i++) { - uint256 hash = block.vtx[i].GetHash(); - if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) + const uint256& hash = block.vtx[i].GetHash(); + if (filter.IsRelevantAndUpdate(block.vtx[i])) { vMatch.push_back(true); vMatchedTxn.push_back(make_pair(i, hash)); @@ -3832,7 +3827,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { mempool.check(pcoinsTip); - RelayTransaction(tx, inv.hash); + RelayTransaction(tx); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); @@ -3862,7 +3857,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx, orphanHash); + RelayTransaction(orphanTx); mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); @@ -3947,7 +3942,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CTransaction tx; bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx, hash)) || + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { diff --git a/src/main.h b/src/main.h index 5a0aedcde1..27041a0026 100644 --- a/src/main.h +++ b/src/main.h @@ -7,7 +7,7 @@ #define BITCOIN_MAIN_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "chainparams.h" @@ -113,7 +113,7 @@ void UnregisterWallet(CWalletInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllWallets(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL); +void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL); /** Register with a network node to receive its signals */ void RegisterNodeSignals(CNodeSignals& nodeSignals); @@ -294,7 +294,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach 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, CTxUndo &txundo, int nHeight, const uint256 &txhash); +void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight); // Context-independent validity checks bool CheckTransaction(const CTransaction& tx, CValidationState& state); @@ -1129,7 +1129,7 @@ public: class CWalletInterface { protected: - virtual void SyncTransaction(const uint256 &hash, const CTransaction &tx, const CBlock *pblock) =0; + virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) =0; virtual void EraseFromWallet(const uint256 &hash) =0; virtual void SetBestChain(const CBlockLocator &locator) =0; virtual void UpdatedTransaction(const uint256 &hash) =0; diff --git a/src/miner.cpp b/src/miner.cpp index 3daf823fd8..6b1f58b229 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -87,14 +87,14 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) CBlock *pblock = &pblocktemplate->block; // pointer for convenience // Create coinbase tx - CTransaction txNew; + CMutableTransaction txNew; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); txNew.vout[0].scriptPubKey = scriptPubKeyIn; - // Add our coinbase tx as first transaction - pblock->vtx.push_back(txNew); + // 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 @@ -256,8 +256,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) continue; CTxUndo txundo; - uint256 hash = tx.GetHash(); - UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash); + const uint256& hash = tx.GetHash(); + UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1); // Added pblock->vtx.push_back(tx); @@ -296,7 +296,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockSize = nBlockSize; LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); - pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + // Compute final coinbase transaction. + txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + txNew.vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; // Fill in header @@ -304,7 +307,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) UpdateTime(*pblock, pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; - pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CBlockIndex indexDummy(*pblock); @@ -330,9 +332,11 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 - pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; - assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + 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(); } diff --git a/src/net.cpp b/src/net.cpp index 757a06aaed..811df43334 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "net.h" @@ -528,10 +528,6 @@ void CNode::CloseSocketDisconnect() pnodeSync = NULL; } -void CNode::Cleanup() -{ -} - void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); @@ -678,7 +674,6 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes) // switch state to reading message data in_data = true; - vRecv.resize(hdr.nMessageSize); return nCopy; } @@ -688,6 +683,11 @@ 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; @@ -773,7 +773,6 @@ void ThreadSocketHandler() // close socket and cleanup pnode->CloseSocketDisconnect(); - pnode->Cleanup(); // hold in disconnected pool until all refs are released if (pnode->fNetworkNode || pnode->fInbound) @@ -1789,6 +1788,7 @@ public: delete pnode; vNodes.clear(); vNodesDisconnected.clear(); + vhListenSocket.clear(); delete semOutbound; semOutbound = NULL; delete pnodeLocalHost; @@ -1808,17 +1808,17 @@ instance_of_cnetcleanup; -void RelayTransaction(const CTransaction& tx, const uint256& hash) +void RelayTransaction(const CTransaction& tx) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(10000); ss << tx; - RelayTransaction(tx, hash, ss); + RelayTransaction(tx, ss); } -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss) +void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { - CInv inv(MSG_TX, hash); + CInv inv(MSG_TX, tx.GetHash()); { LOCK(cs_mapRelay); // Expire old relay messages @@ -1840,7 +1840,7 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt LOCK(pnode->cs_filter); if (pnode->pfilter) { - if (pnode->pfilter->IsRelevantAndUpdate(tx, hash)) + if (pnode->pfilter->IsRelevantAndUpdate(tx)) pnode->PushInventory(inv); } else pnode->PushInventory(inv); @@ -693,8 +693,6 @@ public: void Subscribe(unsigned int nChannel, unsigned int nHops=0); void CancelSubscribe(unsigned int nChannel); void CloseSocketDisconnect(); - void Cleanup(); - // Denial-of-service detection/prevention // The idea is to detect peers that are behaving @@ -726,8 +724,8 @@ public: class CTransaction; -void RelayTransaction(const CTransaction& tx, const uint256& hash); -void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); +void RelayTransaction(const CTransaction& tx); +void RelayTransaction(const CTransaction& tx, const CDataStream& ss); /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/netbase.h b/src/netbase.h index 23cfb1f158..40a3d25676 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -6,7 +6,7 @@ #define BITCOIN_NETBASE_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "compat.h" diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 2dc56a5107..5df8f19729 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "addressbookpage.h" diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 387f6ede4b..76dddb103c 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "bitcoingui.h" diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 275fa35f39..e7a842df99 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,7 +6,7 @@ #define BITCOINGUI_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <QMainWindow> diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 42d6da7d37..c6a6150392 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -440,7 +440,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // nPayAmount qint64 nPayAmount = 0; bool fDust = false; - CTransaction txDummy; + CMutableTransaction txDummy; foreach(const qint64 &amount, CoinControlDialog::payAmounts) { nPayAmount += amount; @@ -521,7 +521,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority); // Fee - int64_t nFee = payTxFee.GetFee(nBytes); + int64_t nFee = payTxFee.GetFee(max((unsigned int)1000, nBytes)); // Min Fee int64_t nMinFee = GetMinFee(txDummy, nBytes, AllowFree(dPriority), GMF_SEND); @@ -582,6 +582,13 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l6->setText(sPriorityLabel); // Priority l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change + if (nPayFee > 0) + { + l3->setText("~" + l3->text()); + l4->setText("~" + l4->text()); + if (nChange > 0) + l8->setText("~" + l8->text()); + } // turn labels "red" l5->setStyleSheet((nBytes >= 1000) ? "color:red;" : ""); // Bytes >= 1000 @@ -599,12 +606,22 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CTransaction::minRelayTxFee.GetFee(546))); + // how many satoshis the estimated fee can vary per byte we guess wrong + double dFeeVary = (double)std::max(CTransaction::minTxFee.GetFeePerK(), payTxFee.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"); diff --git a/src/qt/notificator.h b/src/qt/notificator.h index abab986992..3395e64350 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -6,7 +6,7 @@ #define NOTIFICATOR_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <QIcon> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index abfd4123e0..12d54dff64 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "optionsdialog.h" diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4dafd9d2af..6d985abaf8 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "optionsmodel.h" diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index d8dad15c0d..cc2f00916f 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -22,7 +22,7 @@ #endif #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" /* for USE_QRCODE */ +#include "config/bitcoin-config.h" /* for USE_QRCODE */ #endif #ifdef USE_QRCODE diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index f7491f4a42..e1f40ddd09 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -231,6 +231,7 @@ RPCConsole::RPCConsole(QWidget *parent) : startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); + ui->detailWidget->hide(); clear(); @@ -581,7 +582,7 @@ void RPCConsole::peerLayoutChanged() if (fUnselect && selectedRow >= 0) { ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()), - QItemSelectionModel::Deselect); + QItemSelectionModel::Deselect); } if (fReselect) diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3fee34d00e..3aeff3eace 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -13,7 +13,6 @@ #include <QDialog> class ClientModel; -class CNodeCombinedStats; QT_BEGIN_NAMESPACE class QItemSelection; diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 220da28cfe..03a2381c06 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,5 +1,5 @@ #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #ifdef ENABLE_WALLET diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index dee7daeb2a..9771f8e685 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -349,7 +349,7 @@ Value createrawtransaction(const Array& params, bool fHelp) Array inputs = params[0].get_array(); Object sendTo = params[1].get_obj(); - CTransaction rawTx; + CMutableTransaction rawTx; BOOST_FOREACH(const Value& input, inputs) { @@ -554,11 +554,11 @@ Value signrawtransaction(const Array& params, bool fHelp) vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); - vector<CTransaction> txVariants; + vector<CMutableTransaction> txVariants; while (!ssData.empty()) { try { - CTransaction tx; + CMutableTransaction tx; ssData >> tx; txVariants.push_back(tx); } @@ -572,7 +572,7 @@ Value signrawtransaction(const Array& params, bool fHelp) // mergedTx will end up with all the signatures; it // starts as a clone of the rawtx: - CTransaction mergedTx(txVariants[0]); + CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; // Fetch previous transactions (inputs): @@ -713,7 +713,7 @@ Value signrawtransaction(const Array& params, bool fHelp) SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } @@ -770,7 +770,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) catch (std::exception &e) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); } - uint256 hashTx = tx.GetHash(); + const uint256 &hashTx = tx.GetHash(); CCoinsViewCache &view = *pcoinsTip; CCoins existingCoins; @@ -780,7 +780,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) // push to local node and sync with wallets CValidationState state; if (AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) - SyncWithWallets(hashTx, tx, NULL); + SyncWithWallets(tx, NULL); else { if(state.IsInvalid()) throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); @@ -790,7 +790,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } - RelayTransaction(tx, hashTx); + RelayTransaction(tx); return hashTx.GetHex(); } diff --git a/src/script.cpp b/src/script.cpp index b383a00a6e..c83d26885a 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1636,7 +1636,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1671,7 +1671,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0); } -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1689,7 +1689,7 @@ static CScript PushAll(const vector<valtype>& values) return result; } -static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, +static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn, const vector<valtype>& vSolutions, vector<valtype>& sigs1, vector<valtype>& sigs2) { diff --git a/src/script.h b/src/script.h index a282e7dc04..ea988f0e40 100644 --- a/src/script.h +++ b/src/script.h @@ -20,6 +20,7 @@ class CCoins; class CKeyStore; class CTransaction; +class CMutableTransaction; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes @@ -769,12 +770,12 @@ public: void Serialize(Stream &s, int nType, int nVersion) const { std::vector<unsigned char> compr; if (Compress(compr)) { - s << CFlatData(&compr[0], &compr[compr.size()]); + s << CFlatData(compr); return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); - s << CFlatData(&script[0], &script[script.size()]); + s << CFlatData(script); } template<typename Stream> @@ -783,13 +784,13 @@ public: s >> VARINT(nSize); if (nSize < nSpecialScripts) { std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); - s >> REF(CFlatData(&vch[0], &vch[vch.size()])); + s >> REF(CFlatData(vch)); Decompress(nSize, vch); return; } nSize -= nSpecialScripts; script.resize(nSize); - s >> REF(CFlatData(&script[0], &script[script.size()])); + s >> REF(CFlatData(script)); } }; @@ -805,8 +806,8 @@ bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); // Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders, diff --git a/src/serialize.h b/src/serialize.h index 1341746592..5ac85554c6 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -37,6 +37,34 @@ inline T& REF(const T& val) return const_cast<T&>(val); } +/** Get begin pointer of vector (non-const version). + * @note These functions avoid the undefined case of indexing into an empty + * vector, as well as that of indexing after the end of the vector. + */ +template <class T, class TAl> +inline T* begin_ptr(std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get begin pointer of vector (const version) */ +template <class T, class TAl> +inline const T* begin_ptr(const std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : &v[0]; +} +/** Get end pointer of vector (non-const version) */ +template <class T, class TAl> +inline T* end_ptr(std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} +/** Get end pointer of vector (const version) */ +template <class T, class TAl> +inline const T* end_ptr(const std::vector<T,TAl>& v) +{ + return v.empty() ? NULL : (&v[0] + v.size()); +} + ///////////////////////////////////////////////////////////////// // // Templates for serializing to anything that looks like a stream, @@ -318,6 +346,12 @@ protected: char* pend; public: CFlatData(void* pbeginIn, void* pendIn) : pbegin((char*)pbeginIn), pend((char*)pendIn) { } + template <class T, class TAl> + explicit CFlatData(std::vector<T,TAl> &v) + { + pbegin = (char*)begin_ptr(v); + pend = (char*)end_ptr(v); + } char* begin() { return pbegin; } const char* begin() const { return pbegin; } char* end() { return pend; } diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index fb06fb3435..3a45844411 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) // 50 orphan transactions: for (int i = 0; i < 50; i++) { - CTransaction tx; + CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; tx.vin[0].prevout.hash = GetRandHash(); @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) { CTransaction txPrev = RandomOrphan(); - CTransaction tx; + CMutableTransaction tx; tx.vin.resize(1); tx.vin[0].prevout.n = 0; tx.vin[0].prevout.hash = txPrev.GetHash(); @@ -201,7 +201,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) { CTransaction txPrev = RandomOrphan(); - CTransaction tx; + CMutableTransaction tx; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); @@ -242,10 +242,10 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) // 100 orphan transactions: static const int NPREV=100; - CTransaction orphans[NPREV]; + CMutableTransaction orphans[NPREV]; for (int i = 0; i < NPREV; i++) { - CTransaction& tx = orphans[i]; + CMutableTransaction& tx = orphans[i]; tx.vin.resize(1); tx.vin[0].prevout.n = 0; tx.vin[0].prevout.hash = GetRandHash(); @@ -258,7 +258,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig) } // Create a transaction that depends on orphans: - CTransaction tx; + CMutableTransaction tx; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); diff --git a/src/test/accounting_tests.cpp b/src/test/accounting_tests.cpp index e2a75da349..4bee0f6b6e 100644 --- a/src/test/accounting_tests.cpp +++ b/src/test/accounting_tests.cpp @@ -83,13 +83,21 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; - --wtx.nLockTime; // Just to change the hash :) + { + CMutableTransaction tx(wtx); + --tx.nLockTime; // Just to change the hash :) + *static_cast<CTransaction*>(&wtx) = CTransaction(tx); + } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; wtx.mapValue["comment"] = "x"; - --wtx.nLockTime; // Just to change the hash :) + { + CMutableTransaction tx(wtx); + --tx.nLockTime; // Just to change the hash :) + *static_cast<CTransaction*>(&wtx) = CTransaction(tx); + } pwalletMain->AddToWallet(wtx); vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index abedd3093c..b56d998be2 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -118,33 +118,33 @@ BOOST_AUTO_TEST_CASE(bloom_match) CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); filter.insert(uint256("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b")); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match tx hash"); + 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, tx.GetHash()), "Simple Bloom filter didn't match manually serialized tx hash"); + 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, tx.GetHash()), "Simple Bloom filter didn't match input signature"); + 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, tx.GetHash()), "Simple Bloom filter didn't match input pub key"); + 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, tx.GetHash()), "Simple Bloom filter didn't match output address"); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx, spendingTx.GetHash()), "Simple Bloom filter didn't add output"); + 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, tx.GetHash()), "Simple Bloom filter didn't match output address"); + 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)); - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match COutPoint"); + 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); @@ -154,23 +154,23 @@ BOOST_AUTO_TEST_CASE(bloom_match) memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int)); filter.insert(data); } - BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized COutPoint"); + 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")); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random tx hash"); + 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, tx.GetHash()), "Simple Bloom filter matched random address"); + 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)); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); + 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)); - BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about"); + 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) diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index bff6de41a3..47977cf295 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; CBlockTemplate *pblocktemplate; - CTransaction tx,tx2; + CMutableTransaction tx,tx2; CScript script; uint256 hash; @@ -68,10 +68,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 1; pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; - pblock->vtx[0].vin[0].scriptSig = CScript(); - pblock->vtx[0].vin[0].scriptSig.push_back(blockinfo[i].extranonce); - pblock->vtx[0].vin[0].scriptSig.push_back(chainActive.Height()); - pblock->vtx[0].vout[0].scriptPubKey = CScript(); + CMutableTransaction txCoinbase(pblock->vtx[0]); + txCoinbase.vin[0].scriptSig = CScript(); + txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce); + txCoinbase.vin[0].scriptSig.push_back(chainActive.Height()); + txCoinbase.vout[0].scriptPubKey = CScript(); + pblock->vtx[0] = CTransaction(txCoinbase); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 3775abd633..452cf084a7 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -55,13 +55,13 @@ BOOST_AUTO_TEST_CASE(multisig_verify) CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom; // Funding transaction + CMutableTransaction txFrom; // Funding transaction txFrom.vout.resize(3); txFrom.vout[0].scriptPubKey = a_and_b; txFrom.vout[1].scriptPubKey = a_or_b; txFrom.vout[2].scriptPubKey = escrow; - CTransaction txTo[3]; // Spending transaction + CMutableTransaction txTo[3]; // Spending transaction for (int i = 0; i < 3; i++) { txTo[i].vin.resize(1); @@ -270,13 +270,13 @@ BOOST_AUTO_TEST_CASE(multisig_Sign) CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom; // Funding transaction + CMutableTransaction txFrom; // Funding transaction txFrom.vout.resize(3); txFrom.vout[0].scriptPubKey = a_and_b; txFrom.vout[1].scriptPubKey = a_or_b; txFrom.vout[2].scriptPubKey = escrow; - CTransaction txTo[3]; // Spending transaction + CMutableTransaction txTo[3]; // Spending transaction for (int i = 0; i < 3; i++) { txTo[i].vin.resize(1); diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 7d7e6681df..9dce4daac6 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -36,9 +36,9 @@ BOOST_AUTO_TEST_CASE(pmt_test1) // build a block with some dummy transactions CBlock block; for (unsigned int j=0; j<nTx; j++) { - CTransaction tx; + CMutableTransaction tx; tx.nLockTime = rand(); // actual transaction data doesn't matter; just make the nLockTime's unique - block.vtx.push_back(tx); + block.vtx.push_back(CTransaction(tx)); } // calculate actual merkle root and height diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 9b1290e0ea..a75593a8b2 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -30,11 +30,11 @@ static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) { // Create dummy to/from transactions: - CTransaction txFrom; + CMutableTransaction txFrom; txFrom.vout.resize(1); txFrom.vout[0].scriptPubKey = scriptPubKey; - CTransaction txTo; + CMutableTransaction txTo; txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(sign) evalScripts[i].SetDestination(standardScripts[i].GetID()); } - CTransaction txFrom; // Funding transaction: + CMutableTransaction txFrom; // Funding transaction: string reason; txFrom.vout.resize(8); for (int i = 0; i < 4; i++) @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(sign) } BOOST_CHECK(IsStandardTx(txFrom, reason)); - CTransaction txTo[8]; // Spending transactions + CMutableTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) { txTo[i].vin.resize(1); @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(set) keystore.AddCScript(inner[i]); } - CTransaction txFrom; // Funding transaction: + CMutableTransaction txFrom; // Funding transaction: string reason; txFrom.vout.resize(4); for (int i = 0; i < 4; i++) @@ -183,7 +183,7 @@ BOOST_AUTO_TEST_CASE(set) } BOOST_CHECK(IsStandardTx(txFrom, reason)); - CTransaction txTo[4]; // Spending transactions + CMutableTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) { txTo[i].vin.resize(1); @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) keys.push_back(key[i].GetPubKey()); } - CTransaction txFrom; + CMutableTransaction txFrom; txFrom.vout.resize(6); // First three are standard: @@ -299,7 +299,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0)); - CTransaction txTo; + CMutableTransaction txTo; txTo.vout.resize(1); txTo.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); @@ -326,7 +326,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txTo.vin[i].scriptSig = t; } - CTransaction txToNonStd; + CMutableTransaction txToNonStd; txToNonStd.vout.resize(1); txToNonStd.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID()); txToNonStd.vout[0].nValue = 1000; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 29d518d707..cba582e941 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -240,11 +240,11 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CScript scriptPubKey12; scriptPubKey12 << OP_1 << key1.GetPubKey() << key2.GetPubKey() << OP_2 << OP_CHECKMULTISIG; - CTransaction txFrom12; + CMutableTransaction txFrom12; txFrom12.vout.resize(1); txFrom12.vout[0].scriptPubKey = scriptPubKey12; - CTransaction txTo12; + CMutableTransaction txTo12; txTo12.vin.resize(1); txTo12.vout.resize(1); txTo12.vin[0].prevout.n = 0; @@ -274,11 +274,11 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) CScript scriptPubKey23; scriptPubKey23 << OP_2 << key1.GetPubKey() << key2.GetPubKey() << key3.GetPubKey() << OP_3 << OP_CHECKMULTISIG; - CTransaction txFrom23; + CMutableTransaction txFrom23; txFrom23.vout.resize(1); txFrom23.vout[0].scriptPubKey = scriptPubKey23; - CTransaction txTo23; + CMutableTransaction txTo23; txTo23.vin.resize(1); txTo23.vout.resize(1); txTo23.vin[0].prevout.n = 0; @@ -345,11 +345,11 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) keystore.AddKey(key); } - CTransaction txFrom; + CMutableTransaction txFrom; txFrom.vout.resize(1); txFrom.vout[0].scriptPubKey.SetDestination(keys[0].GetPubKey().GetID()); CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; - CTransaction txTo; + CMutableTransaction txTo; txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 353fd60f7b..423ae4a789 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -28,7 +28,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); return 1; } - CTransaction txTmp(txTo); + 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. @@ -90,7 +90,7 @@ void static RandomScript(CScript &script) { script << oplist[insecure_rand() % (sizeof(oplist)/sizeof(oplist[0]))]; } -void static RandomTransaction(CTransaction &tx, bool fSingle) { +void static RandomTransaction(CMutableTransaction &tx, bool fSingle) { tx.nVersion = insecure_rand(); tx.vin.clear(); tx.vout.clear(); @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(sighash_test) #endif for (int i=0; i<nRandomTests; i++) { int nHashType = insecure_rand(); - CTransaction txTo; + CMutableTransaction txTo; RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); CScript scriptCode; RandomScript(scriptCode); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index feba5ee80e..238033f407 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) 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); - CTransaction tx; + CMutableTransaction tx; stream >> tx; CValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid."); @@ -224,10 +224,10 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) // paid to a TX_PUBKEY, the second 21 and 22 CENT outputs // paid to a TX_PUBKEYHASH. // -static std::vector<CTransaction> +static std::vector<CMutableTransaction> SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet) { - std::vector<CTransaction> dummyTransactions; + std::vector<CMutableTransaction> dummyTransactions; dummyTransactions.resize(2); // Add some keys to the keystore: @@ -261,9 +261,9 @@ BOOST_AUTO_TEST_CASE(test_Get) CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(coinsDummy); - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); + std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); - CTransaction t1; + CMutableTransaction t1; t1.vin.resize(3); t1.vin[0].prevout.hash = dummyTransactions[0].GetHash(); t1.vin[0].prevout.n = 1; @@ -296,9 +296,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) CBasicKeyStore keystore; CCoinsView coinsDummy; CCoinsViewCache coins(coinsDummy); - std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); + std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins); - CTransaction t; + CMutableTransaction t; t.vin.resize(1); t.vin[0].prevout.hash = dummyTransactions[0].GetHash(); t.vin[0].prevout.n = 1; diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index 1993358826..86a83f5163 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -31,16 +31,18 @@ static vector<COutput> vCoins; static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) { static int nextLockTime = 0; - CTransaction tx; + CMutableTransaction tx; tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.vout.resize(nInput+1); tx.vout[nInput].nValue = nValue; + if (fIsFromMe) { + // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), + // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() + tx.vin.resize(1); + } CWalletTx* wtx = new CWalletTx(&wallet, tx); if (fIsFromMe) { - // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), - // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() - wtx->vin.resize(1); wtx->fDebitCached = true; wtx->nDebitCached = 1; } diff --git a/src/util.h b/src/util.h index da1810a3d3..5eb6f03826 100644 --- a/src/util.h +++ b/src/util.h @@ -7,7 +7,7 @@ #define BITCOIN_UTIL_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include "compat.h" diff --git a/src/wallet.cpp b/src/wallet.cpp index 3176cf893a..e74980e9e7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -603,11 +603,11 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // 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 uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { AssertLockHeld(cs_wallet); - bool fExisted = mapWallet.count(hash); + bool fExisted = mapWallet.count(tx.GetHash()); if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { @@ -621,10 +621,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& return false; } -void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock) +void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) { LOCK2(cs_main, cs_wallet); - if (!AddToWalletIfInvolvingMe(hash, tx, pblock, true)) + if (!AddToWalletIfInvolvingMe(tx, pblock, true)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -870,7 +870,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ReadBlockFromDisk(block, pindex); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = chainActive.Next(pindex); @@ -909,9 +909,8 @@ void CWalletTx::RelayWalletTransaction() if (!IsCoinBase()) { if (GetDepthInMainChain() == 0) { - uint256 hash = GetHash(); - LogPrintf("Relaying wtx %s\n", hash.ToString()); - RelayTransaction((CTransaction)*this, hash); + LogPrintf("Relaying wtx %s\n", GetHash().ToString()); + RelayTransaction((CTransaction)*this); } } } @@ -1245,6 +1244,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, } wtxNew.BindWallet(this); + CMutableTransaction txNew; { LOCK2(cs_main, cs_wallet); @@ -1252,8 +1252,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, nFeeRet = payTxFee.GetFeePerK(); while (true) { - wtxNew.vin.clear(); - wtxNew.vout.clear(); + txNew.vin.clear(); + txNew.vout.clear(); wtxNew.fFromMe = true; int64_t nTotalValue = nValue + nFeeRet; @@ -1267,7 +1267,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, strFailReason = _("Transaction amount too small"); return false; } - wtxNew.vout.push_back(txout); + txNew.vout.push_back(txout); } // Choose coins to use @@ -1331,8 +1331,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, else { // Insert change txn at random position: - vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1); - wtxNew.vout.insert(position, newTxOut); + vector<CTxOut>::iterator position = txNew.vout.begin()+GetRandInt(txNew.vout.size()+1); + txNew.vout.insert(position, newTxOut); } } else @@ -1340,17 +1340,20 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64_t> >& vecSend, // Fill vin BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); // Sign int nIn = 0; BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) + 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) @@ -1511,11 +1514,11 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) } -DBErrors CWallet::ZapWalletTx() +DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) { if (!fFileBacked) return DB_LOAD_OK; - DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); + DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); if (nZapWalletTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) diff --git a/src/wallet.h b/src/wallet.h index 7df656fc25..8494ce9a34 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -244,8 +244,8 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false); - void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); - bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); + 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(); @@ -341,7 +341,7 @@ public: void SetBestChain(const CBlockLocator& loc); DBErrors LoadWallet(bool& fFirstRunRet); - DBErrors ZapWalletTx(); + DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 80e9dded5f..3ce2ef019c 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -680,7 +680,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } -DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) { pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; @@ -725,7 +725,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) uint256 hash; ssKey >> hash; + CWalletTx wtx; + ssValue >> wtx; + vTxHash.push_back(hash); + vWtx.push_back(wtx); } } pcursor->close(); @@ -743,11 +747,11 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash) return result; } -DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet) +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx) { // build list of wallet TXs vector<uint256> vTxHash; - DBErrors err = FindWalletTx(pwallet, vTxHash); + DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); if (err != DB_LOAD_OK) return err; diff --git a/src/walletdb.h b/src/walletdb.h index 3bfb436050..8eb716acbb 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -122,8 +122,8 @@ public: DBErrors ReorderTransactions(CWallet*); DBErrors LoadWallet(CWallet* pwallet); - DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash); - DBErrors ZapWalletTx(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); + DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx); static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, std::string filename); }; |