Merge branch '0.10' into merge_btc010
Conflicts: doc/release-notes.md doc/release-process.md
This commit is contained in:
commit
865b63e52d
762
doc/release-notes/release-notes-0.10.0.md
Normal file
762
doc/release-notes/release-notes-0.10.0.md
Normal file
@ -0,0 +1,762 @@
|
||||
Bitcoin Core version 0.10.0 is now available from:
|
||||
|
||||
https://bitcoin.org/bin/0.10.0/
|
||||
|
||||
This is a new major version release, bringing both new features and
|
||||
bug fixes.
|
||||
|
||||
Please report bugs using the issue tracker at github:
|
||||
|
||||
https://github.com/bitcoin/bitcoin/issues
|
||||
|
||||
Upgrading and downgrading
|
||||
=========================
|
||||
|
||||
How to Upgrade
|
||||
--------------
|
||||
|
||||
If you are running an older version, shut it down. Wait until it has completely
|
||||
shut down (which might take a few minutes for older versions), then run the
|
||||
installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or
|
||||
bitcoind/bitcoin-qt (on Linux).
|
||||
|
||||
Downgrading warning
|
||||
---------------------
|
||||
|
||||
Because release 0.10.0 makes use of headers-first synchronization and parallel
|
||||
block download (see further), the block files and databases are not
|
||||
backwards-compatible with older versions of Bitcoin Core or other software:
|
||||
|
||||
* Blocks will be stored on disk out of order (in the order they are
|
||||
received, really), which makes it incompatible with some tools or
|
||||
other programs. Reindexing using earlier versions will also not work
|
||||
anymore as a result of this.
|
||||
|
||||
* The block index database will now hold headers for which no block is
|
||||
stored on disk, which earlier versions won't support.
|
||||
|
||||
If you want to be able to downgrade smoothly, make a backup of your entire data
|
||||
directory. Without this your node will need start syncing (or importing from
|
||||
bootstrap.dat) anew afterwards. It is possible that the data from a completely
|
||||
synchronised 0.10 node may be usable in older versions as-is, but this is not
|
||||
supported and may break as soon as the older version attempts to reindex.
|
||||
|
||||
This does not affect wallet forward or backward compatibility.
|
||||
|
||||
|
||||
Notable changes
|
||||
===============
|
||||
|
||||
Faster synchronization
|
||||
----------------------
|
||||
|
||||
Bitcoin Core now uses 'headers-first synchronization'. This means that we first
|
||||
ask peers for block headers (a total of 27 megabytes, as of December 2014) and
|
||||
validate those. In a second stage, when the headers have been discovered, we
|
||||
download the blocks. However, as we already know about the whole chain in
|
||||
advance, the blocks can be downloaded in parallel from all available peers.
|
||||
|
||||
In practice, this means a much faster and more robust synchronization. On
|
||||
recent hardware with a decent network link, it can be as little as 3 hours
|
||||
for an initial full synchronization. You may notice a slower progress in the
|
||||
very first few minutes, when headers are still being fetched and verified, but
|
||||
it should gain speed afterwards.
|
||||
|
||||
A few RPCs were added/updated as a result of this:
|
||||
- `getblockchaininfo` now returns the number of validated headers in addition to
|
||||
the number of validated blocks.
|
||||
- `getpeerinfo` lists both the number of blocks and headers we know we have in
|
||||
common with each peer. While synchronizing, the heights of the blocks that we
|
||||
have requested from peers (but haven't received yet) are also listed as
|
||||
'inflight'.
|
||||
- A new RPC `getchaintips` lists all known branches of the block chain,
|
||||
including those we only have headers for.
|
||||
|
||||
Transaction fee changes
|
||||
-----------------------
|
||||
|
||||
This release automatically estimates how high a transaction fee (or how
|
||||
high a priority) transactions require to be confirmed quickly. The default
|
||||
settings will create transactions that confirm quickly; see the new
|
||||
'txconfirmtarget' setting to control the tradeoff between fees and
|
||||
confirmation times. Fees are added by default unless the 'sendfreetransactions'
|
||||
setting is enabled.
|
||||
|
||||
Prior releases used hard-coded fees (and priorities), and would
|
||||
sometimes create transactions that took a very long time to confirm.
|
||||
|
||||
Statistics used to estimate fees and priorities are saved in the
|
||||
data directory in the `fee_estimates.dat` file just before
|
||||
program shutdown, and are read in at startup.
|
||||
|
||||
New command line options for transaction fee changes:
|
||||
- `-txconfirmtarget=n` : create transactions that have enough fees (or priority)
|
||||
so they are likely to begin confirmation within n blocks (default: 1). This setting
|
||||
is over-ridden by the -paytxfee option.
|
||||
- `-sendfreetransactions` : Send transactions as zero-fee transactions if possible
|
||||
(default: 0)
|
||||
|
||||
New RPC commands for fee estimation:
|
||||
- `estimatefee nblocks` : Returns approximate fee-per-1,000-bytes needed for
|
||||
a transaction to begin confirmation within nblocks. Returns -1 if not enough
|
||||
transactions have been observed to compute a good estimate.
|
||||
- `estimatepriority nblocks` : Returns approximate priority needed for
|
||||
a zero-fee transaction to begin confirmation within nblocks. Returns -1 if not
|
||||
enough free transactions have been observed to compute a good
|
||||
estimate.
|
||||
|
||||
RPC access control changes
|
||||
--------------------------
|
||||
|
||||
Subnet matching for the purpose of access control is now done
|
||||
by matching the binary network address, instead of with string wildcard matching.
|
||||
For the user this means that `-rpcallowip` takes a subnet specification, which can be
|
||||
|
||||
- a single IP address (e.g. `1.2.3.4` or `fe80::0012:3456:789a:bcde`)
|
||||
- a network/CIDR (e.g. `1.2.3.0/24` or `fe80::0000/64`)
|
||||
- a network/netmask (e.g. `1.2.3.4/255.255.255.0` or `fe80::0012:3456:789a:bcde/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`)
|
||||
|
||||
An arbitrary number of `-rpcallow` arguments can be given. An incoming connection will be accepted if its origin address
|
||||
matches one of them.
|
||||
|
||||
For example:
|
||||
|
||||
| 0.9.x and before | 0.10.x |
|
||||
|--------------------------------------------|---------------------------------------|
|
||||
| `-rpcallowip=192.168.1.1` | `-rpcallowip=192.168.1.1` (unchanged) |
|
||||
| `-rpcallowip=192.168.1.*` | `-rpcallowip=192.168.1.0/24` |
|
||||
| `-rpcallowip=192.168.*` | `-rpcallowip=192.168.0.0/16` |
|
||||
| `-rpcallowip=*` (dangerous!) | `-rpcallowip=::/0` (still dangerous!) |
|
||||
|
||||
Using wildcards will result in the rule being rejected with the following error in debug.log:
|
||||
|
||||
Error: Invalid -rpcallowip subnet specification: *. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).
|
||||
|
||||
|
||||
REST interface
|
||||
--------------
|
||||
|
||||
A new HTTP API is exposed when running with the `-rest` flag, which allows
|
||||
unauthenticated access to public node data.
|
||||
|
||||
It is served on the same port as RPC, but does not need a password, and uses
|
||||
plain HTTP instead of JSON-RPC.
|
||||
|
||||
Assuming a local RPC server running on port 8332, it is possible to request:
|
||||
- Blocks: http://localhost:8332/rest/block/*HASH*.*EXT*
|
||||
- Blocks without transactions: http://localhost:8332/rest/block/notxdetails/*HASH*.*EXT*
|
||||
- Transactions (requires `-txindex`): http://localhost:8332/rest/tx/*HASH*.*EXT*
|
||||
|
||||
In every case, *EXT* can be `bin` (for raw binary data), `hex` (for hex-encoded
|
||||
binary) or `json`.
|
||||
|
||||
For more details, see the `doc/REST-interface.md` document in the repository.
|
||||
|
||||
RPC Server "Warm-Up" Mode
|
||||
-------------------------
|
||||
|
||||
The RPC server is started earlier now, before most of the expensive
|
||||
intialisations like loading the block index. It is available now almost
|
||||
immediately after starting the process. However, until all initialisations
|
||||
are done, it always returns an immediate error with code -28 to all calls.
|
||||
|
||||
This new behaviour can be useful for clients to know that a server is already
|
||||
started and will be available soon (for instance, so that they do not
|
||||
have to start it themselves).
|
||||
|
||||
Improved signing security
|
||||
-------------------------
|
||||
|
||||
For 0.10 the security of signing against unusual attacks has been
|
||||
improved by making the signatures constant time and deterministic.
|
||||
|
||||
This change is a result of switching signing to use libsecp256k1
|
||||
instead of OpenSSL. Libsecp256k1 is a cryptographic library
|
||||
optimized for the curve Bitcoin uses which was created by Bitcoin
|
||||
Core developer Pieter Wuille.
|
||||
|
||||
There exist attacks[1] against most ECC implementations where an
|
||||
attacker on shared virtual machine hardware could extract a private
|
||||
key if they could cause a target to sign using the same key hundreds
|
||||
of times. While using shared hosts and reusing keys are inadvisable
|
||||
for other reasons, it's a better practice to avoid the exposure.
|
||||
|
||||
OpenSSL has code in their source repository for derandomization
|
||||
and reduction in timing leaks that we've eagerly wanted to use for a
|
||||
long time, but this functionality has still not made its
|
||||
way into a released version of OpenSSL. Libsecp256k1 achieves
|
||||
significantly stronger protection: As far as we're aware this is
|
||||
the only deployed implementation of constant time signing for
|
||||
the curve Bitcoin uses and we have reason to believe that
|
||||
libsecp256k1 is better tested and more thoroughly reviewed
|
||||
than the implementation in OpenSSL.
|
||||
|
||||
[1] https://eprint.iacr.org/2014/161.pdf
|
||||
|
||||
Watch-only wallet support
|
||||
-------------------------
|
||||
|
||||
The wallet can now track transactions to and from wallets for which you know
|
||||
all addresses (or scripts), even without the private keys.
|
||||
|
||||
This can be used to track payments without needing the private keys online on a
|
||||
possibly vulnerable system. In addition, it can help for (manual) construction
|
||||
of multisig transactions where you are only one of the signers.
|
||||
|
||||
One new RPC, `importaddress`, is added which functions similarly to
|
||||
`importprivkey`, but instead takes an address or script (in hexadecimal) as
|
||||
argument. After using it, outputs credited to this address or script are
|
||||
considered to be received, and transactions consuming these outputs will be
|
||||
considered to be sent.
|
||||
|
||||
The following RPCs have optional support for watch-only:
|
||||
`getbalance`, `listreceivedbyaddress`, `listreceivedbyaccount`,
|
||||
`listtransactions`, `listaccounts`, `listsinceblock`, `gettransaction`. See the
|
||||
RPC documentation for those methods for more information.
|
||||
|
||||
Compared to using `getrawtransaction`, this mechanism does not require
|
||||
`-txindex`, scales better, integrates better with the wallet, and is compatible
|
||||
with future block chain pruning functionality. It does mean that all relevant
|
||||
addresses need to added to the wallet before the payment, though.
|
||||
|
||||
Consensus library
|
||||
-----------------
|
||||
|
||||
Starting from 0.10.0, the Bitcoin Core distribution includes a consensus library.
|
||||
|
||||
The purpose of this library is to make the verification functionality that is
|
||||
critical to Bitcoin's consensus available to other applications, e.g. to language
|
||||
bindings such as [python-bitcoinlib](https://pypi.python.org/pypi/python-bitcoinlib) or
|
||||
alternative node implementations.
|
||||
|
||||
This library is called `libbitcoinconsensus.so` (or, `.dll` for Windows).
|
||||
Its interface is defined in the C header [bitcoinconsensus.h](https://github.com/bitcoin/bitcoin/blob/0.10/src/script/bitcoinconsensus.h).
|
||||
|
||||
In its initial version the API includes two functions:
|
||||
|
||||
- `bitcoinconsensus_verify_script` verifies a script. It returns whether the indicated input of the provided serialized transaction
|
||||
correctly spends the passed scriptPubKey under additional constraints indicated by flags
|
||||
- `bitcoinconsensus_version` returns the API version, currently at an experimental `0`
|
||||
|
||||
The functionality is planned to be extended to e.g. UTXO management in upcoming releases, but the interface
|
||||
for existing methods should remain stable.
|
||||
|
||||
Standard script rules relaxed for P2SH addresses
|
||||
------------------------------------------------
|
||||
|
||||
The IsStandard() rules have been almost completely removed for P2SH
|
||||
redemption scripts, allowing applications to make use of any valid
|
||||
script type, such as "n-of-m OR y", hash-locked oracle addresses, etc.
|
||||
While the Bitcoin protocol has always supported these types of script,
|
||||
actually using them on mainnet has been previously inconvenient as
|
||||
standard Bitcoin Core nodes wouldn't relay them to miners, nor would
|
||||
most miners include them in blocks they mined.
|
||||
|
||||
bitcoin-tx
|
||||
----------
|
||||
|
||||
It has been observed that many of the RPC functions offered by bitcoind are
|
||||
"pure functions", and operate independently of the bitcoind wallet. This
|
||||
included many of the RPC "raw transaction" API functions, such as
|
||||
createrawtransaction.
|
||||
|
||||
bitcoin-tx is a newly introduced command line utility designed to enable easy
|
||||
manipulation of bitcoin transactions. A summary of its operation may be
|
||||
obtained via "bitcoin-tx --help" Transactions may be created or signed in a
|
||||
manner similar to the RPC raw tx API. Transactions may be updated, deleting
|
||||
inputs or outputs, or appending new inputs and outputs. Custom scripts may be
|
||||
easily composed using a simple text notation, borrowed from the bitcoin test
|
||||
suite.
|
||||
|
||||
This tool may be used for experimenting with new transaction types, signing
|
||||
multi-party transactions, and many other uses. Long term, the goal is to
|
||||
deprecate and remove "pure function" RPC API calls, as those do not require a
|
||||
server round-trip to execute.
|
||||
|
||||
Other utilities "bitcoin-key" and "bitcoin-script" have been proposed, making
|
||||
key and script operations easily accessible via command line.
|
||||
|
||||
Mining and relay policy enhancements
|
||||
------------------------------------
|
||||
|
||||
Bitcoin Core's block templates are now for version 3 blocks only, and any mining
|
||||
software relying on its `getblocktemplate` must be updated in parallel to use
|
||||
libblkmaker either version 0.4.2 or any version from 0.5.1 onward.
|
||||
If you are solo mining, this will affect you the moment you upgrade Bitcoin
|
||||
Core, which must be done prior to BIP66 achieving its 951/1001 status.
|
||||
If you are mining with the stratum mining protocol: this does not affect you.
|
||||
If you are mining with the getblocktemplate protocol to a pool: this will affect
|
||||
you at the pool operator's discretion, which must be no later than BIP66
|
||||
achieving its 951/1001 status.
|
||||
|
||||
The `prioritisetransaction` RPC method has been added to enable miners to
|
||||
manipulate the priority of transactions on an individual basis.
|
||||
|
||||
Bitcoin Core now supports BIP 22 long polling, so mining software can be
|
||||
notified immediately of new templates rather than having to poll periodically.
|
||||
|
||||
Support for BIP 23 block proposals is now available in Bitcoin Core's
|
||||
`getblocktemplate` method. This enables miners to check the basic validity of
|
||||
their next block before expending work on it, reducing risks of accidental
|
||||
hardforks or mining invalid blocks.
|
||||
|
||||
Two new options to control mining policy:
|
||||
- `-datacarrier=0/1` : Relay and mine "data carrier" (OP_RETURN) transactions
|
||||
if this is 1.
|
||||
- `-datacarriersize=n` : Maximum size, in bytes, we consider acceptable for
|
||||
"data carrier" outputs.
|
||||
|
||||
The relay policy has changed to more properly implement the desired behavior of not
|
||||
relaying free (or very low fee) transactions unless they have a priority above the
|
||||
AllowFreeThreshold(), in which case they are relayed subject to the rate limiter.
|
||||
|
||||
BIP 66: strict DER encoding for signatures
|
||||
------------------------------------------
|
||||
|
||||
Bitcoin Core 0.10 implements BIP 66, which introduces block version 3, and a new
|
||||
consensus rule, which prohibits non-DER signatures. Such transactions have been
|
||||
non-standard since Bitcoin v0.8.0 (released in February 2013), but were
|
||||
technically still permitted inside blocks.
|
||||
|
||||
This change breaks the dependency on OpenSSL's signature parsing, and is
|
||||
required if implementations would want to remove all of OpenSSL from the
|
||||
consensus code.
|
||||
|
||||
The same miner-voting mechanism as in BIP 34 is used: when 751 out of a
|
||||
sequence of 1001 blocks have version number 3 or higher, the new consensus
|
||||
rule becomes active for those blocks. When 951 out of a sequence of 1001
|
||||
blocks have version number 3 or higher, it becomes mandatory for all blocks.
|
||||
|
||||
Backward compatibility with current mining software is NOT provided, thus miners
|
||||
should read the first paragraph of "Mining and relay policy enhancements" above.
|
||||
|
||||
0.10.0 Change log
|
||||
=================
|
||||
|
||||
Detailed release notes follow. This overview includes changes that affect external
|
||||
behavior, not code moves, refactors or string updates.
|
||||
|
||||
RPC:
|
||||
- `f923c07` Support IPv6 lookup in bitcoin-cli even when IPv6 only bound on localhost
|
||||
- `b641c9c` Fix addnode "onetry": Connect with OpenNetworkConnection
|
||||
- `171ca77` estimatefee / estimatepriority RPC methods
|
||||
- `b750cf1` Remove cli functionality from bitcoind
|
||||
- `f6984e8` Add "chain" to getmininginfo, improve help in getblockchaininfo
|
||||
- `99ddc6c` Add nLocalServices info to RPC getinfo
|
||||
- `cf0c47b` Remove getwork() RPC call
|
||||
- `2a72d45` prioritisetransaction <txid> <priority delta> <priority tx fee>
|
||||
- `e44fea5` Add an option `-datacarrier` to allow users to disable relaying/mining data carrier transactions
|
||||
- `2ec5a3d` Prevent easy RPC memory exhaustion attack
|
||||
- `d4640d7` Added argument to getbalance to include watchonly addresses and fixed errors in balance calculation
|
||||
- `83f3543` Added argument to listaccounts to include watchonly addresses
|
||||
- `952877e` Showing 'involvesWatchonly' property for transactions returned by 'listtransactions' and 'listsinceblock'. It is only appended when the transaction involves a watchonly address
|
||||
- `d7d5d23` Added argument to listtransactions and listsinceblock to include watchonly addresses
|
||||
- `f87ba3d` added includeWatchonly argument to 'gettransaction' because it affects balance calculation
|
||||
- `0fa2f88` added includedWatchonly argument to listreceivedbyaddress/...account
|
||||
- `6c37f7f` `getrawchangeaddress`: fail when keypool exhausted and wallet locked
|
||||
- `ff6a7af` getblocktemplate: longpolling support
|
||||
- `c4a321f` Add peerid to getpeerinfo to allow correlation with the logs
|
||||
- `1b4568c` Add vout to ListTransactions output
|
||||
- `b33bd7a` Implement "getchaintips" RPC command to monitor blockchain forks
|
||||
- `733177e` Remove size limit in RPC client, keep it in server
|
||||
- `6b5b7cb` Categorize rpc help overview
|
||||
- `6f2c26a` Closely track mempool byte total. Add "getmempoolinfo" RPC
|
||||
- `aa82795` Add detailed network info to getnetworkinfo RPC
|
||||
- `01094bd` Don't reveal whether password is <20 or >20 characters in RPC
|
||||
- `57153d4` rpc: Compute number of confirmations of a block from block height
|
||||
- `ff36cbe` getnetworkinfo: export local node's client sub-version string
|
||||
- `d14d7de` SanitizeString: allow '(' and ')'
|
||||
- `31d6390` Fixed setaccount accepting foreign address
|
||||
- `b5ec5fe` update getnetworkinfo help with subversion
|
||||
- `ad6e601` RPC additions after headers-first
|
||||
- `33dfbf5` rpc: Fix leveldb iterator leak, and flush before `gettxoutsetinfo`
|
||||
- `2aa6329` Enable customising node policy for datacarrier data size with a -datacarriersize option
|
||||
- `f877aaa` submitblock: Use a temporary CValidationState to determine accurately the outcome of ProcessBlock
|
||||
- `e69a587` submitblock: Support for returning specific rejection reasons
|
||||
- `af82884` Add "warmup mode" for RPC server
|
||||
- `e2655e0` Add unauthenticated HTTP REST interface to public blockchain data
|
||||
- `683dc40` Disable SSLv3 (in favor of TLS) for the RPC client and server
|
||||
- `44b4c0d` signrawtransaction: validate private key
|
||||
- `9765a50` Implement BIP 23 Block Proposal
|
||||
- `f9de17e` Add warning comment to getinfo
|
||||
|
||||
Command-line options:
|
||||
- `ee21912` Use netmasks instead of wildcards for IP address matching
|
||||
- `deb3572` Add `-rpcbind` option to allow binding RPC port on a specific interface
|
||||
- `96b733e` Add `-version` option to get just the version
|
||||
- `1569353` Add `-stopafterblockimport` option
|
||||
- `77cbd46` Let -zapwallettxes recover transaction meta data
|
||||
- `1c750db` remove -tor compatibility code (only allow -onion)
|
||||
- `4aaa017` rework help messages for fee-related options
|
||||
- `4278b1d` Clarify error message when invalid -rpcallowip
|
||||
- `6b407e4` -datadir is now allowed in config files
|
||||
- `bdd5b58` Add option `-sysperms` to disable 077 umask (create new files with system default umask)
|
||||
- `cbe39a3` Add "bitcoin-tx" command line utility and supporting modules
|
||||
- `dbca89b` Trigger -alertnotify if network is upgrading without you
|
||||
- `ad96e7c` Make -reindex cope with out-of-order blocks
|
||||
- `16d5194` Skip reindexed blocks individually
|
||||
- `ec01243` --tracerpc option for regression tests
|
||||
- `f654f00` Change -genproclimit default to 1
|
||||
- `3c77714` Make -proxy set all network types, avoiding a connect leak
|
||||
- `57be955` Remove -printblock, -printblocktree, and -printblockindex
|
||||
- `ad3d208` remove -maxorphanblocks config parameter since it is no longer functional
|
||||
|
||||
Block and transaction handling:
|
||||
- `7a0e84d` ProcessGetData(): abort if a block file is missing from disk
|
||||
- `8c93bf4` LoadBlockIndexDB(): Require block db reindex if any `blk*.dat` files are missing
|
||||
- `77339e5` Get rid of the static chainMostWork (optimization)
|
||||
- `4e0eed8` Allow ActivateBestChain to release its lock on cs_main
|
||||
- `18e7216` Push cs_mains down in ProcessBlock
|
||||
- `fa126ef` Avoid undefined behavior using CFlatData in CScript serialization
|
||||
- `7f3b4e9` Relax IsStandard rules for pay-to-script-hash transactions
|
||||
- `c9a0918` Add a skiplist to the CBlockIndex structure
|
||||
- `bc42503` Use unordered_map for CCoinsViewCache with salted hash (optimization)
|
||||
- `d4d3fbd` Do not flush the cache after every block outside of IBD (optimization)
|
||||
- `ad08d0b` Bugfix: make CCoinsViewMemPool support pruned entries in underlying cache
|
||||
- `5734d4d` Only remove actualy failed blocks from setBlockIndexValid
|
||||
- `d70bc52` Rework block processing benchmark code
|
||||
- `714a3e6` Only keep setBlockIndexValid entries that are possible improvements
|
||||
- `ea100c7` Reduce maximum coinscache size during verification (reduce memory usage)
|
||||
- `4fad8e6` Reject transactions with excessive numbers of sigops
|
||||
- `b0875eb` Allow BatchWrite to destroy its input, reducing copying (optimization)
|
||||
- `92bb6f2` Bypass reloading blocks from disk (optimization)
|
||||
- `2e28031` Perform CVerifyDB on pcoinsdbview instead of pcoinsTip (reduce memory usage)
|
||||
- `ab15b2e` Avoid copying undo data (optimization)
|
||||
- `341735e` Headers-first synchronization
|
||||
- `afc32c5` Fix rebuild-chainstate feature and improve its performance
|
||||
- `e11b2ce` Fix large reorgs
|
||||
- `ed6d1a2` Keep information about all block files in memory
|
||||
- `a48f2d6` Abstract context-dependent block checking from acceptance
|
||||
- `7e615f5` Fixed mempool sync after sending a transaction
|
||||
- `51ce901` Improve chainstate/blockindex disk writing policy
|
||||
- `a206950` Introduce separate flushing modes
|
||||
- `9ec75c5` Add a locking mechanism to IsInitialBlockDownload to ensure it never goes from false to true
|
||||
- `868d041` Remove coinbase-dependant transactions during reorg
|
||||
- `723d12c` Remove txn which are invalidated by coinbase maturity during reorg
|
||||
- `0cb8763` Check against MANDATORY flags prior to accepting to mempool
|
||||
- `8446262` Reject headers that build on an invalid parent
|
||||
- `008138c` Bugfix: only track UTXO modification after lookup
|
||||
|
||||
P2P protocol and network code:
|
||||
- `f80cffa` Do not trigger a DoS ban if SCRIPT_VERIFY_NULLDUMMY fails
|
||||
- `c30329a` Add testnet DNS seed of Alex Kotenko
|
||||
- `45a4baf` Add testnet DNS seed of Andreas Schildbach
|
||||
- `f1920e8` Ping automatically every 2 minutes (unconditionally)
|
||||
- `806fd19` Allocate receive buffers in on the fly
|
||||
- `6ecf3ed` Display unknown commands received
|
||||
- `aa81564` Track peers' available blocks
|
||||
- `caf6150` Use async name resolving to improve net thread responsiveness
|
||||
- `9f4da19` Use pong receive time rather than processing time
|
||||
- `0127a9b` remove SOCKS4 support from core and GUI, use SOCKS5
|
||||
- `40f5cb8` Send rejects and apply DoS scoring for errors in direct block validation
|
||||
- `dc942e6` Introduce whitelisted peers
|
||||
- `c994d2e` prevent SOCKET leak in BindListenPort()
|
||||
- `a60120e` Add built-in seeds for .onion
|
||||
- `60dc8e4` Allow -onlynet=onion to be used
|
||||
- `3a56de7` addrman: Do not propagate obviously poor addresses onto the network
|
||||
- `6050ab6` netbase: Make SOCKS5 negotiation interruptible
|
||||
- `604ee2a` Remove tx from AlreadyAskedFor list once we receive it, not when we process it
|
||||
- `efad808` Avoid reject message feedback loops
|
||||
- `71697f9` Separate protocol versioning from clientversion
|
||||
- `20a5f61` Don't relay alerts to peers before version negotiation
|
||||
- `b4ee0bd` Introduce preferred download peers
|
||||
- `845c86d` Do not use third party services for IP detection
|
||||
- `12a49ca` Limit the number of new addressses to accumulate
|
||||
- `35e408f` Regard connection failures as attempt for addrman
|
||||
- `a3a7317` Introduce 10 minute block download timeout
|
||||
- `3022e7d` Require sufficent priority for relay of free transactions
|
||||
- `58fda4d` Update seed IPs, based on bitcoin.sipa.be crawler data
|
||||
- `18021d0` Remove bitnodes.io from dnsseeds.
|
||||
|
||||
Validation:
|
||||
- `6fd7ef2` Also switch the (unused) verification code to low-s instead of even-s
|
||||
- `584a358` Do merkle root and txid duplicates check simultaneously
|
||||
- `217a5c9` When transaction outputs exceed inputs, show the offending amounts so as to aid debugging
|
||||
- `f74fc9b` Print input index when signature validation fails, to aid debugging
|
||||
- `6fd59ee` script.h: set_vch() should shift a >32 bit value
|
||||
- `d752ba8` Add SCRIPT_VERIFY_SIGPUSHONLY (BIP62 rule 2) (test only)
|
||||
- `698c6ab` Add SCRIPT_VERIFY_MINIMALDATA (BIP62 rules 3 and 4) (test only)
|
||||
- `ab9edbd` script: create sane error return codes for script validation and remove logging
|
||||
- `219a147` script: check ScriptError values in script tests
|
||||
- `0391423` Discourage NOPs reserved for soft-fork upgrades
|
||||
- `98b135f` Make STRICTENC invalid pubkeys fail the script rather than the opcode
|
||||
- `307f7d4` Report script evaluation failures in log and reject messages
|
||||
- `ace39db` consensus: guard against openssl's new strict DER checks
|
||||
- `12b7c44` Improve robustness of DER recoding code
|
||||
- `76ce5c8` fail immediately on an empty signature
|
||||
|
||||
Build system:
|
||||
- `f25e3ad` Fix build in OS X 10.9
|
||||
- `65e8ba4` build: Switch to non-recursive make
|
||||
- `460b32d` build: fix broken boost chrono check on some platforms
|
||||
- `9ce0774` build: Fix windows configure when using --with-qt-libdir
|
||||
- `ea96475` build: Add mention of --disable-wallet to bdb48 error messages
|
||||
- `1dec09b` depends: add shared dependency builder
|
||||
- `c101c76` build: Add --with-utils (bitcoin-cli and bitcoin-tx, default=yes). Help string consistency tweaks. Target sanity check fix
|
||||
- `e432a5f` build: add option for reducing exports (v2)
|
||||
- `6134b43` Fixing condition 'sabotaging' MSVC build
|
||||
- `af0bd5e` osx: fix signing to make Gatekeeper happy (again)
|
||||
- `a7d1f03` build: fix dynamic boost check when --with-boost= is used
|
||||
- `d5fd094` build: fix qt test build when libprotobuf is in a non-standard path
|
||||
- `2cf5f16` Add libbitcoinconsensus library
|
||||
- `914868a` build: add a deterministic dmg signer
|
||||
- `2d375fe` depends: bump openssl to 1.0.1k
|
||||
- `b7a4ecc` Build: Only check for boost when building code that requires it
|
||||
|
||||
Wallet:
|
||||
- `b33d1f5` Use fee/priority estimates in wallet CreateTransaction
|
||||
- `4b7b1bb` Sanity checks for estimates
|
||||
- `c898846` Add support for watch-only addresses
|
||||
- `d5087d1` Use script matching rather than destination matching for watch-only
|
||||
- `d88af56` Fee fixes
|
||||
- `a35b55b` Dont run full check every time we decrypt wallet
|
||||
- `3a7c348` Fix make_change to not create half-satoshis
|
||||
- `f606bb9` fix a possible memory leak in CWalletDB::Recover
|
||||
- `870da77` fix possible memory leaks in CWallet::EncryptWallet
|
||||
- `ccca27a` Watch-only fixes
|
||||
- `9b1627d` [Wallet] Reduce minTxFee for transaction creation to 1000 satoshis
|
||||
- `a53fd41` Deterministic signing
|
||||
- `15ad0b5` Apply AreSane() checks to the fees from the network
|
||||
- `11855c1` Enforce minRelayTxFee on wallet created tx and add a maxtxfee option
|
||||
|
||||
GUI:
|
||||
- `c21c74b` osx: Fix missing dock menu with qt5
|
||||
- `b90711c` Fix Transaction details shows wrong To:
|
||||
- `516053c` Make links in 'About Bitcoin Core' clickable
|
||||
- `bdc83e8` Ensure payment request network matches client network
|
||||
- `65f78a1` Add GUI view of peer information
|
||||
- `06a91d9` VerifyDB progress reporting
|
||||
- `fe6bff2` Add BerkeleyDB version info to RPCConsole
|
||||
- `b917555` PeerTableModel: Fix potential deadlock. #4296
|
||||
- `dff0e3b` Improve rpc console history behavior
|
||||
- `95a9383` Remove CENT-fee-rule from coin control completely
|
||||
- `56b07d2` Allow setting listen via GUI
|
||||
- `d95ba75` Log messages with type>QtDebugMsg as non-debug
|
||||
- `8969828` New status bar Unit Display Control and related changes
|
||||
- `674c070` seed OpenSSL PNRG with Windows event data
|
||||
- `509f926` Payment request parsing on startup now only changes network if a valid network name is specified
|
||||
- `acd432b` Prevent balloon-spam after rescan
|
||||
- `7007402` Implement SI-style (thin space) thoudands separator
|
||||
- `91cce17` Use fixed-point arithmetic in amount spinbox
|
||||
- `bdba2dd` Remove an obscure option no-one cares about
|
||||
- `bd0aa10` Replace the temporary file hack currently used to change Bitcoin-Qt's dock icon (OS X) with a buffer-based solution
|
||||
- `94e1b9e` Re-work overviewpage UI
|
||||
- `8bfdc9a` Better looking trayicon
|
||||
- `b197bf3` disable tray interactions when client model set to 0
|
||||
- `1c5f0af` Add column Watch-only to transactions list
|
||||
- `21f139b` Fix tablet crash. closes #4854
|
||||
- `e84843c` Broken addresses on command line no longer trigger testnet
|
||||
- `a49f11d` Change splash screen to normal window
|
||||
- `1f9be98` Disable App Nap on OSX 10.9+
|
||||
- `27c3e91` Add proxy to options overridden if necessary
|
||||
- `4bd1185` Allow "emergency" shutdown during startup
|
||||
- `d52f072` Don't show wallet options in the preferences menu when running with -disablewallet
|
||||
- `6093aa1` Qt: QProgressBar CPU-Issue workaround
|
||||
- `0ed9675` [Wallet] Add global boolean whether to send free transactions (default=true)
|
||||
- `ed3e5e4` [Wallet] Add global boolean whether to pay at least the custom fee (default=true)
|
||||
- `e7876b2` [Wallet] Prevent user from paying a non-sense fee
|
||||
- `c1c9d5b` Add Smartfee to GUI
|
||||
- `e0a25c5` Make askpassphrase dialog behave more sanely
|
||||
- `94b362d` On close of splashscreen interrupt verifyDB
|
||||
- `b790d13` English translation update
|
||||
- `8543b0d` Correct tooltip on address book page
|
||||
|
||||
Tests:
|
||||
- `b41e594` Fix script test handling of empty scripts
|
||||
- `d3a33fc` Test CHECKMULTISIG with m == 0 and n == 0
|
||||
- `29c1749` Let tx (in)valid tests use any SCRIPT_VERIFY flag
|
||||
- `6380180` Add rejection of non-null CHECKMULTISIG dummy values
|
||||
- `21bf3d2` Add tests for BoostAsioToCNetAddr
|
||||
- `b5ad5e7` Add Python test for -rpcbind and -rpcallowip
|
||||
- `9ec0306` Add CODESEPARATOR/FindAndDelete() tests
|
||||
- `75ebced` Added many rpc wallet tests
|
||||
- `0193fb8` Allow multiple regression tests to run at once
|
||||
- `92a6220` Hook up sanity checks
|
||||
- `3820e01` Extend and move all crypto tests to crypto_tests.cpp
|
||||
- `3f9a019` added list/get received by address/ account tests
|
||||
- `a90689f` Remove timing-based signature cache unit test
|
||||
- `236982c` Add skiplist unit tests
|
||||
- `f4b00be` Add CChain::GetLocator() unit test
|
||||
- `b45a6e8` Add test for getblocktemplate longpolling
|
||||
- `cdf305e` Set -discover=0 in regtest framework
|
||||
- `ed02282` additional test for OP_SIZE in script_valid.json
|
||||
- `0072d98` script tests: BOOLAND, BOOLOR decode to integer
|
||||
- `833ff16` script tests: values that overflow to 0 are true
|
||||
- `4cac5db` script tests: value with trailing 0x00 is true
|
||||
- `89101c6` script test: test case for 5-byte bools
|
||||
- `d2d9dc0` script tests: add tests for CHECKMULTISIG limits
|
||||
- `d789386` Add "it works" test for bitcoin-tx
|
||||
- `df4d61e` Add bitcoin-tx tests
|
||||
- `aa41ac2` Test IsPushOnly() with invalid push
|
||||
- `6022b5d` Make `script_{valid,invalid}.json` validation flags configurable
|
||||
- `8138cbe` Add automatic script test generation, and actual checksig tests
|
||||
- `ed27e53` Add coins_tests with a large randomized CCoinViewCache test
|
||||
- `9df9cf5` Make SCRIPT_VERIFY_STRICTENC compatible with BIP62
|
||||
- `dcb9846` Extend getchaintips RPC test
|
||||
- `554147a` Ensure MINIMALDATA invalid tests can only fail one way
|
||||
- `dfeec18` Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule
|
||||
- `2b62e17` Clearly separate PUSHDATA and numeric argument MINIMALDATA tests
|
||||
- `16d78bd` Add valid invert of invalid every numeric opcode tests
|
||||
- `f635269` tests: enable alertnotify test for Windows
|
||||
- `7a41614` tests: allow rpc-tests to get filenames for bitcoind and bitcoin-cli from the environment
|
||||
- `5122ea7` tests: fix forknotify.py on windows
|
||||
- `fa7f8cd` tests: remove old pull-tester scripts
|
||||
- `7667850` tests: replace the old (unused since Travis) tests with new rpc test scripts
|
||||
- `f4e0aef` Do signature-s negation inside the tests
|
||||
- `1837987` Optimize -regtest setgenerate block generation
|
||||
- `2db4c8a` Fix node ranges in the test framework
|
||||
- `a8b2ce5` regression test only setmocktime RPC call
|
||||
- `daf03e7` RPC tests: create initial chain with specific timestamps
|
||||
- `8656dbb` Port/fix txnmall.sh regression test
|
||||
- `ca81587` Test the exact order of CHECKMULTISIG sig/pubkey evaluation
|
||||
- `7357893` Prioritize and display -testsafemode status in UI
|
||||
- `f321d6b` Add key generation/verification to ECC sanity check
|
||||
- `132ea9b` miner_tests: Disable checkpoints so they don't fail the subsidy-change test
|
||||
- `bc6cb41` QA RPC tests: Add tests block block proposals
|
||||
- `f67a9ce` Use deterministically generated script tests
|
||||
- `11d7a7d` [RPC] add rpc-test for http keep-alive (persistent connections)
|
||||
- `34318d7` RPC-test based on invalidateblock for mempool coinbase spends
|
||||
- `76ec867` Use actually valid transactions for script tests
|
||||
- `c8589bf` Add actual signature tests
|
||||
- `e2677d7` Fix smartfees test for change to relay policy
|
||||
- `263b65e` tests: run sanity checks in tests too
|
||||
|
||||
Miscellaneous:
|
||||
- `122549f` Fix incorrect checkpoint data for testnet3
|
||||
- `5bd02cf` Log used config file to debug.log on startup
|
||||
- `68ba85f` Updated Debian example bitcoin.conf with config from wiki + removed some cruft and updated comments
|
||||
- `e5ee8f0` Remove -beta suffix
|
||||
- `38405ac` Add comment regarding experimental-use service bits
|
||||
- `be873f6` Issue warning if collecting RandSeed data failed
|
||||
- `8ae973c` Allocate more space if necessary in RandSeedAddPerfMon
|
||||
- `675bcd5` Correct comment for 15-of-15 p2sh script size
|
||||
- `fda3fed` libsecp256k1 integration
|
||||
- `2e36866` Show nodeid instead of addresses in log (for anonymity) unless otherwise requested
|
||||
- `cd01a5e` Enable paranoid corruption checks in LevelDB >= 1.16
|
||||
- `9365937` Add comment about never updating nTimeOffset past 199 samples
|
||||
- `403c1bf` contrib: remove getwork-based pyminer (as getwork API call has been removed)
|
||||
- `0c3e101` contrib: Added systemd .service file in order to help distributions integrate bitcoind
|
||||
- `0a0878d` doc: Add new DNSseed policy
|
||||
- `2887bff` Update coding style and add .clang-format
|
||||
- `5cbda4f` Changed LevelDB cursors to use scoped pointers to ensure destruction when going out of scope
|
||||
- `b4a72a7` contrib/linearize: split output files based on new-timestamp-year or max-file-size
|
||||
- `e982b57` Use explicit fflush() instead of setvbuf()
|
||||
- `234bfbf` contrib: Add init scripts and docs for Upstart and OpenRC
|
||||
- `01c2807` Add warning about the merkle-tree algorithm duplicate txid flaw
|
||||
- `d6712db` Also create pid file in non-daemon mode
|
||||
- `772ab0e` contrib: use batched JSON-RPC in linarize-hashes (optimization)
|
||||
- `7ab4358` Update bash-completion for v0.10
|
||||
- `6e6a36c` contrib: show pull # in prompt for github-merge script
|
||||
- `5b9f842` Upgrade leveldb to 1.18, make chainstate databases compatible between ARM and x86 (issue #2293)
|
||||
- `4e7c219` Catch UTXO set read errors and shutdown
|
||||
- `867c600` Catch LevelDB errors during flush
|
||||
- `06ca065` Fix CScriptID(const CScript& in) in empty script case
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Thanks to everyone who contributed to this release:
|
||||
|
||||
- 21E14
|
||||
- Adam Weiss
|
||||
- Aitor Pazos
|
||||
- Alexander Jeng
|
||||
- Alex Morcos
|
||||
- Alon Muroch
|
||||
- Andreas Schildbach
|
||||
- Andrew Poelstra
|
||||
- Andy Alness
|
||||
- Ashley Holman
|
||||
- Benedict Chan
|
||||
- Ben Holden-Crowther
|
||||
- Bryan Bishop
|
||||
- BtcDrak
|
||||
- Christian von Roques
|
||||
- Clinton Christian
|
||||
- Cory Fields
|
||||
- Cozz Lovan
|
||||
- daniel
|
||||
- Daniel Kraft
|
||||
- David Hill
|
||||
- Derek701
|
||||
- dexX7
|
||||
- dllud
|
||||
- Dominyk Tiller
|
||||
- Doug
|
||||
- elichai
|
||||
- elkingtowa
|
||||
- ENikS
|
||||
- Eric Shaw
|
||||
- Federico Bond
|
||||
- Francis GASCHET
|
||||
- Gavin Andresen
|
||||
- Giuseppe Mazzotta
|
||||
- Glenn Willen
|
||||
- Gregory Maxwell
|
||||
- gubatron
|
||||
- HarryWu
|
||||
- himynameismartin
|
||||
- Huang Le
|
||||
- Ian Carroll
|
||||
- imharrywu
|
||||
- Jameson Lopp
|
||||
- Janusz Lenar
|
||||
- JaSK
|
||||
- Jeff Garzik
|
||||
- JL2035
|
||||
- Johnathan Corgan
|
||||
- Jonas Schnelli
|
||||
- jtimon
|
||||
- Julian Haight
|
||||
- Kamil Domanski
|
||||
- kazcw
|
||||
- kevin
|
||||
- kiwigb
|
||||
- Kosta Zertsekel
|
||||
- LongShao007
|
||||
- Luke Dashjr
|
||||
- Mark Friedenbach
|
||||
- Mathy Vanvoorden
|
||||
- Matt Corallo
|
||||
- Matthew Bogosian
|
||||
- Micha
|
||||
- Michael Ford
|
||||
- Mike Hearn
|
||||
- mrbandrews
|
||||
- mruddy
|
||||
- ntrgn
|
||||
- Otto Allmendinger
|
||||
- paveljanik
|
||||
- Pavel Vasin
|
||||
- Peter Todd
|
||||
- phantomcircuit
|
||||
- Philip Kaufmann
|
||||
- Pieter Wuille
|
||||
- pryds
|
||||
- randy-waterhouse
|
||||
- R E Broadley
|
||||
- Rose Toomey
|
||||
- Ross Nicoll
|
||||
- Roy Badami
|
||||
- Ruben Dario Ponticelli
|
||||
- Rune K. Svendsen
|
||||
- Ryan X. Charles
|
||||
- Saivann
|
||||
- sandakersmann
|
||||
- SergioDemianLerner
|
||||
- shshshsh
|
||||
- sinetek
|
||||
- Stuart Cardall
|
||||
- Suhas Daftuar
|
||||
- Tawanda Kembo
|
||||
- Teran McKinney
|
||||
- tm314159
|
||||
- Tom Harding
|
||||
- Trevin Hofmann
|
||||
- Whit J
|
||||
- Wladimir J. van der Laan
|
||||
- Yoichi Hirai
|
||||
- Zak Wilcox
|
||||
|
||||
As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/).
|
||||
|
@ -81,13 +81,6 @@ Release Process
|
||||
5. Gitian signatures (in gitian.sigs/${VERSION}-<linux|win|osx-unsigned>/(your gitian key)/
|
||||
|
||||
###Next steps:
|
||||
pushd gitian.sigs
|
||||
git add ${VERSION}-linux/${SIGNER}
|
||||
git add ${VERSION}-win/${SIGNER}
|
||||
git add ${VERSION}-osx/${SIGNER}
|
||||
git commit -a
|
||||
git push # Assuming you can push to the gitian.sigs tree
|
||||
popd
|
||||
|
||||
Commit your signature to gitian.sigs:
|
||||
|
||||
@ -121,6 +114,7 @@ Commit your signature for the signed OSX binary:
|
||||
git push # Assuming you can push to the gitian.sigs tree
|
||||
popd
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
### After 3 or more people have gitian-built and their results match:
|
||||
|
||||
@ -139,15 +133,22 @@ rm SHA256SUMS
|
||||
(the digest algorithm is forced to sha256 to avoid confusion of the `Hash:` header that GPG adds with the SHA256 used for the files)
|
||||
|
||||
- Upload zips and installers, as well as `SHA256SUMS.asc` from last step, to the bitcoin.org server
|
||||
into `/var/www/bin/bitcoin-core-${VERSION}`
|
||||
|
||||
- Update dashpay.io version
|
||||
- Update dashpay.io version ***TODO***
|
||||
|
||||
- Make a pull request to add a file named `YYYY-MM-DD-vX.Y.Z.md` with the release notes
|
||||
to https://github.com/dashpay/dashpay.io/tree/master/_releases ***TODO***
|
||||
([Example for 0.9.2.1](https://raw.githubusercontent.com/dashpay/dashpay.io/master/_releases/2014-06-19-v0.9.2.1.md)). ***TODO***
|
||||
- First, check to see if the dashpay.io maintainers have prepared a
|
||||
release: https://github.com/bitcoin/bitcoin.org/labels/Releases
|
||||
|
||||
- After the pull request is merged, the website will automatically show the newest version, as well
|
||||
as update the OS download links. Ping Saivann in case anything goes wrong
|
||||
- If they have, it will have previously failed their Travis CI
|
||||
checks because the final release files weren't uploaded.
|
||||
Trigger a Travis CI rebuild---if it passes, merge.
|
||||
|
||||
- If they have not prepared a release, follow the Bitcoin.org release
|
||||
instructions: https://github.com/bitcoin/bitcoin.org#release-notes
|
||||
|
||||
- After the pull request is merged, the website will automatically show the newest version within 15 minutes, as well
|
||||
as update the OS download links. Ping @saivann/@harding (saivann/harding on Freenode) in case anything goes wrong
|
||||
|
||||
- Announce the release:
|
||||
|
||||
|
@ -51,6 +51,7 @@ BITCOIN_TESTS =\
|
||||
test/hash_tests.cpp \
|
||||
test/key_tests.cpp \
|
||||
test/main_tests.cpp \
|
||||
test/mempool_tests.cpp \
|
||||
test/miner_tests.cpp \
|
||||
test/mruset_tests.cpp \
|
||||
test/multisig_tests.cpp \
|
||||
|
293
src/addrman.cpp
293
src/addrman.cpp
@ -10,34 +10,27 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
int CAddrInfo::GetTriedBucket(const std::vector<unsigned char>& nKey) const
|
||||
int CAddrInfo::GetTriedBucket(const uint256& 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();
|
||||
|
||||
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 hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetLow64();
|
||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetLow64();
|
||||
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
int CAddrInfo::GetNewBucket(const std::vector<unsigned char>& nKey, const CNetAddr& src) const
|
||||
int CAddrInfo::GetNewBucket(const uint256& 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();
|
||||
|
||||
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 hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetLow64();
|
||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetLow64();
|
||||
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
|
||||
{
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetHash().GetLow64();
|
||||
return hash1 % ADDRMAN_BUCKET_SIZE;
|
||||
}
|
||||
|
||||
bool CAddrInfo::IsTerrible(int64_t nNow) const
|
||||
{
|
||||
if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
|
||||
@ -70,8 +63,6 @@ double CAddrInfo::GetChance(int64_t nNow) const
|
||||
if (nSinceLastTry < 0)
|
||||
nSinceLastTry = 0;
|
||||
|
||||
fChance *= 600.0 / (600.0 + nSinceLastSeen);
|
||||
|
||||
// deprioritize very recent attempts away
|
||||
if (nSinceLastTry < 60 * 10)
|
||||
fChance *= 0.01;
|
||||
@ -128,85 +119,44 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
|
||||
vRandom[nRndPos2] = nId1;
|
||||
}
|
||||
|
||||
int CAddrMan::SelectTried(int nKBucket)
|
||||
void CAddrMan::Delete(int nId)
|
||||
{
|
||||
std::vector<int>& vTried = vvTried[nKBucket];
|
||||
assert(mapInfo.count(nId) != 0);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
assert(!info.fInTried);
|
||||
assert(info.nRefCount == 0);
|
||||
|
||||
// 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;
|
||||
SwapRandom(info.nRandomPos, vRandom.size() - 1);
|
||||
vRandom.pop_back();
|
||||
mapAddr.erase(info);
|
||||
mapInfo.erase(nId);
|
||||
nNew--;
|
||||
}
|
||||
|
||||
int CAddrMan::ShrinkNew(int nUBucket)
|
||||
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
|
||||
{
|
||||
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;
|
||||
// if there is an entry in the specified bucket, delete it.
|
||||
if (vvNew[nUBucket][nUBucketPos] != -1) {
|
||||
int nIdDelete = vvNew[nUBucket][nUBucketPos];
|
||||
CAddrInfo& infoDelete = mapInfo[nIdDelete];
|
||||
assert(infoDelete.nRefCount > 0);
|
||||
infoDelete.nRefCount--;
|
||||
vvNew[nUBucket][nUBucketPos] = -1;
|
||||
if (infoDelete.nRefCount == 0) {
|
||||
Delete(nIdDelete);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
|
||||
{
|
||||
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))
|
||||
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
int pos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (vvNew[bucket][pos] == nId) {
|
||||
vvNew[bucket][pos] = -1;
|
||||
info.nRefCount--;
|
||||
}
|
||||
}
|
||||
nNew--;
|
||||
|
||||
@ -214,44 +164,36 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
|
||||
|
||||
// which tried bucket to move the entry to
|
||||
int nKBucket = info.GetTriedBucket(nKey);
|
||||
std::vector<int>& vTried = vvTried[nKBucket];
|
||||
int nKBucketPos = info.GetBucketPosition(nKey, false, 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;
|
||||
// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
|
||||
if (vvTried[nKBucket][nKBucketPos] != -1) {
|
||||
// find an item to evict
|
||||
int nIdEvict = vvTried[nKBucket][nKBucketPos];
|
||||
assert(mapInfo.count(nIdEvict) == 1);
|
||||
CAddrInfo& infoOld = mapInfo[nIdEvict];
|
||||
|
||||
// Remove the to-be-evicted item from the tried set.
|
||||
infoOld.fInTried = false;
|
||||
vvTried[nKBucket][nKBucketPos] = -1;
|
||||
nTried--;
|
||||
|
||||
// find which new bucket it belongs to
|
||||
int nUBucket = infoOld.GetNewBucket(nKey);
|
||||
int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
|
||||
ClearNew(nUBucket, nUBucketPos);
|
||||
assert(vvNew[nUBucket][nUBucketPos] == -1);
|
||||
|
||||
// Enter it into the new set again.
|
||||
infoOld.nRefCount = 1;
|
||||
vvNew[nUBucket][nUBucketPos] = nIdEvict;
|
||||
nNew++;
|
||||
}
|
||||
assert(vvTried[nKBucket][nKBucketPos] == -1);
|
||||
|
||||
// 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
|
||||
vvTried[nKBucket][nKBucketPos] = nId;
|
||||
nTried++;
|
||||
info.fInTried = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void CAddrMan::Good_(const CService& addr, int64_t nTime)
|
||||
@ -281,12 +223,12 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime)
|
||||
return;
|
||||
|
||||
// find a bucket it is in now
|
||||
int nRnd = GetRandInt(vvNew.size());
|
||||
int nRnd = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
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)) {
|
||||
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
||||
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
int nBpos = info.GetBucketPosition(nKey, true, nB);
|
||||
if (vvNew[nB][nBpos] == nId) {
|
||||
nUBucket = nB;
|
||||
break;
|
||||
}
|
||||
@ -300,7 +242,7 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime)
|
||||
LogPrint("addrman", "Moving %s to tried\n", addr.ToString());
|
||||
|
||||
// move nId to the tried tables
|
||||
MakeTried(info, nId, nUBucket);
|
||||
MakeTried(info, nId);
|
||||
}
|
||||
|
||||
bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
|
||||
@ -348,12 +290,25 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
|
||||
}
|
||||
|
||||
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);
|
||||
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
|
||||
if (vvNew[nUBucket][nUBucketPos] != nId) {
|
||||
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
|
||||
if (!fInsert) {
|
||||
CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
|
||||
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
|
||||
// Overwrite the existing new table entry.
|
||||
fInsert = true;
|
||||
}
|
||||
}
|
||||
if (fInsert) {
|
||||
ClearNew(nUBucket, nUBucketPos);
|
||||
pinfo->nRefCount++;
|
||||
vvNew[nUBucket][nUBucketPos] = nId;
|
||||
} else {
|
||||
if (pinfo->nRefCount == 0) {
|
||||
Delete(nId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fNew;
|
||||
}
|
||||
@ -377,24 +332,23 @@ void CAddrMan::Attempt_(const CService& addr, int64_t nTime)
|
||||
info.nAttempts++;
|
||||
}
|
||||
|
||||
CAddress CAddrMan::Select_(int nUnkBias)
|
||||
CAddress CAddrMan::Select_()
|
||||
{
|
||||
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 50% chance for choosing between tried and new table entries.
|
||||
if (nTried > 0 && (nNew == 0 || GetRandInt(2) == 0)) {
|
||||
// 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)
|
||||
int nKBucket = GetRandInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||
int nKBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE);
|
||||
if (vvTried[nKBucket][nKBucketPos] == -1)
|
||||
continue;
|
||||
int nPos = GetRandInt(vTried.size());
|
||||
assert(mapInfo.count(vTried[nPos]) == 1);
|
||||
CAddrInfo& info = mapInfo[vTried[nPos]];
|
||||
int nId = vvTried[nKBucket][nKBucketPos];
|
||||
assert(mapInfo.count(nId) == 1);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
@ -403,16 +357,13 @@ CAddress CAddrMan::Select_(int nUnkBias)
|
||||
// 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)
|
||||
int nUBucket = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
int nUBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE);
|
||||
if (vvNew[nUBucket][nUBucketPos] == -1)
|
||||
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];
|
||||
int nId = vvNew[nUBucket][nUBucketPos];
|
||||
assert(mapInfo.count(nId) == 1);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
@ -460,22 +411,30 @@ int CAddrMan::Check_()
|
||||
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 < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvTried[n][i] != -1) {
|
||||
if (!setTried.count(vvTried[n][i]))
|
||||
return -11;
|
||||
if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey) != n)
|
||||
return -17;
|
||||
if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
|
||||
return -18;
|
||||
setTried.erase(vvTried[n][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[n][i] != -1) {
|
||||
if (!mapNew.count(vvNew[n][i]))
|
||||
return -12;
|
||||
if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
|
||||
return -19;
|
||||
if (--mapNew[vvNew[n][i]] == 0)
|
||||
mapNew.erase(vvNew[n][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,6 +442,8 @@ int CAddrMan::Check_()
|
||||
return -13;
|
||||
if (mapNew.size())
|
||||
return -15;
|
||||
if (nKey.IsNull())
|
||||
return -16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
208
src/addrman.h
208
src/addrman.h
@ -79,17 +79,20 @@ public:
|
||||
}
|
||||
|
||||
//! Calculate in which "tried" bucket this entry belongs
|
||||
int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
|
||||
int GetTriedBucket(const uint256 &nKey) const;
|
||||
|
||||
//! Calculate in which "new" bucket this entry belongs, given a certain source
|
||||
int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
|
||||
int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
|
||||
|
||||
//! Calculate in which "new" bucket this entry belongs, using its default source
|
||||
int GetNewBucket(const std::vector<unsigned char> &nKey) const
|
||||
int GetNewBucket(const uint256 &nKey) const
|
||||
{
|
||||
return GetNewBucket(nKey, source);
|
||||
}
|
||||
|
||||
//! Calculate in which position of a bucket to store this entry.
|
||||
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
|
||||
|
||||
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
|
||||
bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
|
||||
|
||||
@ -106,15 +109,15 @@ public:
|
||||
*
|
||||
* To that end:
|
||||
* * Addresses are organized into buckets.
|
||||
* * Address that have not yet been tried go into 256 "new" buckets.
|
||||
* * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random
|
||||
* * Address that have not yet been tried go into 1024 "new" buckets.
|
||||
* * Based on the address range (/16 for IPv4) of source of the information, 64 buckets are selected at random
|
||||
* * The actual bucket is chosen from one of these, based on the range the address itself is located.
|
||||
* * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that
|
||||
* * One single address can occur in up to 8 different buckets, to increase selection chances for addresses that
|
||||
* are seen frequently. The chance for increasing this multiplicity decreases exponentially.
|
||||
* * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
|
||||
* ones) is removed from it first.
|
||||
* * Addresses of nodes that are known to be accessible go into 64 "tried" buckets.
|
||||
* * Each address range selects at random 4 of these buckets.
|
||||
* * Addresses of nodes that are known to be accessible go into 256 "tried" buckets.
|
||||
* * Each address range selects at random 8 of these buckets.
|
||||
* * The actual bucket is chosen from one of these, based on the full address.
|
||||
* * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
|
||||
* tried ones) is evicted from it, back to the "new" buckets.
|
||||
@ -125,28 +128,22 @@ public:
|
||||
*/
|
||||
|
||||
//! total number of buckets for tried addresses
|
||||
#define ADDRMAN_TRIED_BUCKET_COUNT 64
|
||||
|
||||
//! maximum allowed number of entries in buckets for tried addresses
|
||||
#define ADDRMAN_TRIED_BUCKET_SIZE 64
|
||||
#define ADDRMAN_TRIED_BUCKET_COUNT 256
|
||||
|
||||
//! total number of buckets for new addresses
|
||||
#define ADDRMAN_NEW_BUCKET_COUNT 256
|
||||
#define ADDRMAN_NEW_BUCKET_COUNT 1024
|
||||
|
||||
//! maximum allowed number of entries in buckets for new addresses
|
||||
#define ADDRMAN_NEW_BUCKET_SIZE 64
|
||||
//! maximum allowed number of entries in buckets for new and tried addresses
|
||||
#define ADDRMAN_BUCKET_SIZE 64
|
||||
|
||||
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
|
||||
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
|
||||
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
|
||||
|
||||
//! over how many buckets entries with new addresses originating from a single group are spread
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
|
||||
|
||||
//! in how many buckets for entries with new addresses a single address may occur
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
|
||||
|
||||
//! how many entries in a bucket with tried addresses are inspected, when selecting one to replace
|
||||
#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
|
||||
|
||||
//! how old addresses can maximally be
|
||||
#define ADDRMAN_HORIZON_DAYS 30
|
||||
@ -176,7 +173,7 @@ private:
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
//! secret key to randomize bucket select with
|
||||
std::vector<unsigned char> nKey;
|
||||
uint256 nKey;
|
||||
|
||||
//! last used nId
|
||||
int nIdCount;
|
||||
@ -194,13 +191,13 @@ private:
|
||||
int nTried;
|
||||
|
||||
//! list of "tried" buckets
|
||||
std::vector<std::vector<int> > vvTried;
|
||||
int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
|
||||
|
||||
//! number of (unique) "new" entries
|
||||
int nNew;
|
||||
|
||||
//! list of "new" buckets
|
||||
std::vector<std::set<int> > vvNew;
|
||||
int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
|
||||
|
||||
protected:
|
||||
|
||||
@ -214,17 +211,14 @@ protected:
|
||||
//! Swap two elements in vRandom.
|
||||
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
|
||||
|
||||
//! Return position in given bucket to replace.
|
||||
int SelectTried(int nKBucket);
|
||||
|
||||
//! Remove an element from a "new" bucket.
|
||||
//! This is the only place where actual deletions occur.
|
||||
//! Elements are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
|
||||
int ShrinkNew(int nUBucket);
|
||||
|
||||
//! Move an entry from the "new" table(s) to the "tried" table
|
||||
//! @pre vvUnkown[nOrigin].count(nId) != 0
|
||||
void MakeTried(CAddrInfo& info, int nId, int nOrigin);
|
||||
void MakeTried(CAddrInfo& info, int nId);
|
||||
|
||||
//! Delete an entry. It must not be in tried, and have refcount 0.
|
||||
void Delete(int nId);
|
||||
|
||||
//! Clear a position in a "new" table. This is the only place where entries are actually deleted.
|
||||
void ClearNew(int nUBucket, int nUBucketPos);
|
||||
|
||||
//! Mark an entry "good", possibly moving it from "new" to "tried".
|
||||
void Good_(const CService &addr, int64_t nTime);
|
||||
@ -237,7 +231,7 @@ protected:
|
||||
|
||||
//! Select an address to connect to.
|
||||
//! nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
|
||||
CAddress Select_(int nUnkBias);
|
||||
CAddress Select_();
|
||||
|
||||
#ifdef DEBUG_ADDRMAN
|
||||
//! Perform consistency check. Returns an error code or zero.
|
||||
@ -253,17 +247,21 @@ protected:
|
||||
public:
|
||||
/**
|
||||
* serialized format:
|
||||
* * version byte (currently 0)
|
||||
* * nKey
|
||||
* * version byte (currently 1)
|
||||
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
||||
* * nNew
|
||||
* * nTried
|
||||
* * number of "new" buckets
|
||||
* * number of "new" buckets XOR 2**30
|
||||
* * all nNew addrinfos in vvNew
|
||||
* * all nTried addrinfos in vvTried
|
||||
* * for each bucket:
|
||||
* * number of elements
|
||||
* * for each element: index
|
||||
*
|
||||
* 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
|
||||
* as incompatible. This is necessary because it did not check the version number on
|
||||
* deserialization.
|
||||
*
|
||||
* Notice that vvTried, mapAddr and vVector are never encoded explicitly;
|
||||
* they are instead reconstructed from the other information.
|
||||
*
|
||||
@ -275,48 +273,53 @@ public:
|
||||
*
|
||||
* We don't use ADD_SERIALIZE_METHODS since the serialization and deserialization code has
|
||||
* very little in common.
|
||||
*
|
||||
*/
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s, int nType, int nVersionDummy) const
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
unsigned char nVersion = 0;
|
||||
unsigned char nVersion = 1;
|
||||
s << nVersion;
|
||||
s << ((unsigned char)32);
|
||||
s << nKey;
|
||||
s << nNew;
|
||||
s << nTried;
|
||||
|
||||
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
|
||||
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
|
||||
s << nUBuckets;
|
||||
std::map<int, int> mapUnkIds;
|
||||
int nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
|
||||
if (nIds == nNew) break; // this means nNew was wrong, oh ow
|
||||
mapUnkIds[(*it).first] = nIds;
|
||||
const CAddrInfo &info = (*it).second;
|
||||
if (info.nRefCount) {
|
||||
assert(nIds != nNew); // this means nNew was wrong, oh ow
|
||||
s << info;
|
||||
nIds++;
|
||||
}
|
||||
}
|
||||
nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
|
||||
if (nIds == nTried) break; // this means nTried was wrong, oh ow
|
||||
const CAddrInfo &info = (*it).second;
|
||||
if (info.fInTried) {
|
||||
assert(nIds != nTried); // this means nTried was wrong, oh ow
|
||||
s << info;
|
||||
nIds++;
|
||||
}
|
||||
}
|
||||
for (std::vector<std::set<int> >::const_iterator it = vvNew.begin(); it != vvNew.end(); it++) {
|
||||
const std::set<int> &vNew = (*it);
|
||||
int nSize = vNew.size();
|
||||
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
int nSize = 0;
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[bucket][i] != -1)
|
||||
nSize++;
|
||||
}
|
||||
s << nSize;
|
||||
for (std::set<int>::const_iterator it2 = vNew.begin(); it2 != vNew.end(); it2++) {
|
||||
int nIndex = mapUnkIds[*it2];
|
||||
s << nIndex;
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[bucket][i] != -1) {
|
||||
int nIndex = mapUnkIds[vvNew[bucket][i]];
|
||||
s << nIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,64 +329,97 @@ public:
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
Clear();
|
||||
|
||||
unsigned char nVersion;
|
||||
s >> nVersion;
|
||||
unsigned char nKeySize;
|
||||
s >> nKeySize;
|
||||
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
||||
s >> nKey;
|
||||
s >> nNew;
|
||||
s >> nTried;
|
||||
|
||||
int nUBuckets = 0;
|
||||
s >> nUBuckets;
|
||||
nIdCount = 0;
|
||||
mapInfo.clear();
|
||||
mapAddr.clear();
|
||||
vRandom.clear();
|
||||
vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
|
||||
vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
|
||||
if (nVersion != 0) {
|
||||
nUBuckets ^= (1 << 30);
|
||||
}
|
||||
|
||||
// Deserialize entries from the new table.
|
||||
for (int n = 0; n < nNew; n++) {
|
||||
CAddrInfo &info = mapInfo[n];
|
||||
s >> info;
|
||||
mapAddr[info] = n;
|
||||
info.nRandomPos = vRandom.size();
|
||||
vRandom.push_back(n);
|
||||
if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
|
||||
vvNew[info.GetNewBucket(nKey)].insert(n);
|
||||
info.nRefCount++;
|
||||
if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
|
||||
// In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
|
||||
// immediately try to give them a reference based on their primary source address.
|
||||
int nUBucket = info.GetNewBucket(nKey);
|
||||
int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
|
||||
if (vvNew[nUBucket][nUBucketPos] == -1) {
|
||||
vvNew[nUBucket][nUBucketPos] = n;
|
||||
info.nRefCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
nIdCount = nNew;
|
||||
|
||||
// Deserialize entries from the tried table.
|
||||
int nLost = 0;
|
||||
for (int n = 0; n < nTried; n++) {
|
||||
CAddrInfo info;
|
||||
s >> info;
|
||||
std::vector<int> &vTried = vvTried[info.GetTriedBucket(nKey)];
|
||||
if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE) {
|
||||
int nKBucket = info.GetTriedBucket(nKey);
|
||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||
if (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||
info.nRandomPos = vRandom.size();
|
||||
info.fInTried = true;
|
||||
vRandom.push_back(nIdCount);
|
||||
mapInfo[nIdCount] = info;
|
||||
mapAddr[info] = nIdCount;
|
||||
vTried.push_back(nIdCount);
|
||||
vvTried[nKBucket][nKBucketPos] = nIdCount;
|
||||
nIdCount++;
|
||||
} else {
|
||||
nLost++;
|
||||
}
|
||||
}
|
||||
nTried -= nLost;
|
||||
for (int b = 0; b < nUBuckets; b++) {
|
||||
std::set<int> &vNew = vvNew[b];
|
||||
|
||||
// Deserialize positions in the new table (if possible).
|
||||
for (int bucket = 0; bucket < nUBuckets; bucket++) {
|
||||
int nSize = 0;
|
||||
s >> nSize;
|
||||
for (int n = 0; n < nSize; n++) {
|
||||
int nIndex = 0;
|
||||
s >> nIndex;
|
||||
CAddrInfo &info = mapInfo[nIndex];
|
||||
if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
|
||||
info.nRefCount++;
|
||||
vNew.insert(nIndex);
|
||||
if (nIndex >= 0 && nIndex < nNew) {
|
||||
CAddrInfo &info = mapInfo[nIndex];
|
||||
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
|
||||
info.nRefCount++;
|
||||
vvNew[bucket][nUBucketPos] = nIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prune new entries with refcount 0 (as a result of collisions).
|
||||
int nLostUnk = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
|
||||
if (it->second.fInTried == false && it->second.nRefCount == 0) {
|
||||
std::map<int, CAddrInfo>::const_iterator itCopy = it++;
|
||||
Delete(itCopy->first);
|
||||
nLostUnk++;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
if (nLost + nLostUnk > 0) {
|
||||
LogPrint("addrman", "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
|
||||
}
|
||||
|
||||
Check();
|
||||
}
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const
|
||||
@ -391,14 +427,34 @@ public:
|
||||
return (CSizeComputer(nType, nVersion) << *this).size();
|
||||
}
|
||||
|
||||
CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
|
||||
void Clear()
|
||||
{
|
||||
nKey.resize(32);
|
||||
GetRandBytes(&nKey[0], 32);
|
||||
std::vector<int>().swap(vRandom);
|
||||
nKey = GetRandHash();
|
||||
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
||||
vvNew[bucket][entry] = -1;
|
||||
}
|
||||
}
|
||||
for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
|
||||
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
||||
vvTried[bucket][entry] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
nIdCount = 0;
|
||||
nTried = 0;
|
||||
nNew = 0;
|
||||
nIdCount = 0;
|
||||
nTried = 0;
|
||||
nNew = 0;
|
||||
}
|
||||
|
||||
CAddrMan()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
~CAddrMan()
|
||||
{
|
||||
nKey = uint256(0);
|
||||
}
|
||||
|
||||
//! Return the number of (unique) addresses in all tables.
|
||||
@ -477,13 +533,13 @@ public:
|
||||
* Choose an address to connect to.
|
||||
* nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
|
||||
*/
|
||||
CAddress Select(int nUnkBias = 50)
|
||||
CAddress Select()
|
||||
{
|
||||
CAddress addrRet;
|
||||
{
|
||||
LOCK(cs);
|
||||
Check();
|
||||
addrRet = Select_(nUnkBias);
|
||||
addrRet = Select_();
|
||||
Check();
|
||||
}
|
||||
return addrRet;
|
||||
|
@ -172,8 +172,8 @@ public:
|
||||
|
||||
fRequireRPCPassword = true;
|
||||
fMiningRequiresPeers = true;
|
||||
fDefaultCheckMemPool = false;
|
||||
fAllowMinDifficultyBlocks = false;
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = true;
|
||||
fMineBlocksOnDemand = false;
|
||||
fSkipProofOfWorkCheck = false;
|
||||
@ -241,8 +241,8 @@ public:
|
||||
|
||||
fRequireRPCPassword = true;
|
||||
fMiningRequiresPeers = true;
|
||||
fDefaultCheckMemPool = false;
|
||||
fAllowMinDifficultyBlocks = true;
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = false;
|
||||
fMineBlocksOnDemand = false;
|
||||
fTestnetToBeDeprecatedFieldRPC = true;
|
||||
@ -292,8 +292,8 @@ public:
|
||||
|
||||
fRequireRPCPassword = false;
|
||||
fMiningRequiresPeers = false;
|
||||
fDefaultCheckMemPool = true;
|
||||
fAllowMinDifficultyBlocks = true;
|
||||
fDefaultConsistencyChecks = true;
|
||||
fRequireStandard = false;
|
||||
fMineBlocksOnDemand = true;
|
||||
fTestnetToBeDeprecatedFieldRPC = false;
|
||||
@ -319,7 +319,7 @@ public:
|
||||
|
||||
fRequireRPCPassword = false;
|
||||
fMiningRequiresPeers = false;
|
||||
fDefaultCheckMemPool = true;
|
||||
fDefaultConsistencyChecks = true;
|
||||
fAllowMinDifficultyBlocks = false;
|
||||
fMineBlocksOnDemand = true;
|
||||
}
|
||||
@ -335,7 +335,7 @@ public:
|
||||
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 setDefaultConsistencyChecks(bool afDefaultConsistencyChecks) { fDefaultConsistencyChecks=afDefaultConsistencyChecks; }
|
||||
virtual void setAllowMinDifficultyBlocks(bool afAllowMinDifficultyBlocks) { fAllowMinDifficultyBlocks=afAllowMinDifficultyBlocks; }
|
||||
virtual void setSkipProofOfWorkCheck(bool afSkipProofOfWorkCheck) { fSkipProofOfWorkCheck = afSkipProofOfWorkCheck; }
|
||||
};
|
||||
|
@ -59,8 +59,8 @@ public:
|
||||
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; }
|
||||
/** Default value for -checkmempool and -checkblockindex argument */
|
||||
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
|
||||
/** Allow mining of a min-difficulty block */
|
||||
bool AllowMinDifficultyBlocks() const { return fAllowMinDifficultyBlocks; }
|
||||
/** Skip proof-of-work check: allow mining of any difficulty block */
|
||||
@ -110,8 +110,8 @@ protected:
|
||||
std::vector<CAddress> vFixedSeeds;
|
||||
bool fRequireRPCPassword;
|
||||
bool fMiningRequiresPeers;
|
||||
bool fDefaultCheckMemPool;
|
||||
bool fAllowMinDifficultyBlocks;
|
||||
bool fDefaultConsistencyChecks;
|
||||
bool fRequireStandard;
|
||||
bool fMineBlocksOnDemand;
|
||||
bool fSkipProofOfWorkCheck;
|
||||
@ -136,7 +136,7 @@ public:
|
||||
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 setDefaultConsistencyChecks(bool aDefaultConsistencyChecks)=0;
|
||||
virtual void setAllowMinDifficultyBlocks(bool aAllowMinDifficultyBlocks)=0;
|
||||
virtual void setSkipProofOfWorkCheck(bool aSkipProofOfWorkCheck)=0;
|
||||
};
|
||||
|
@ -687,8 +687,9 @@ bool AppInit2(boost::thread_group& threadGroup)
|
||||
if (GetBoolArg("-benchmark", false))
|
||||
InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench."));
|
||||
|
||||
// Checkmempool defaults to true in regtest mode
|
||||
mempool.setSanityCheck(GetBoolArg("-checkmempool", Params().DefaultCheckMemPool()));
|
||||
// Checkmempool and checkblockindex default to true in regtest mode
|
||||
mempool.setSanityCheck(GetBoolArg("-checkmempool", Params().DefaultConsistencyChecks()));
|
||||
fCheckBlockIndex = GetBoolArg("-checkblockindex", Params().DefaultConsistencyChecks());
|
||||
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
|
||||
|
||||
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
||||
|
147
src/main.cpp
147
src/main.cpp
@ -57,6 +57,7 @@ bool fImporting = false;
|
||||
bool fReindex = false;
|
||||
bool fTxIndex = false;
|
||||
bool fIsBareMultisigStd = true;
|
||||
bool fCheckBlockIndex = false;
|
||||
unsigned int nCoinCacheSize = 5000;
|
||||
|
||||
|
||||
@ -73,6 +74,8 @@ map<uint256, COrphanTx> mapOrphanTransactions;
|
||||
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
|
||||
void EraseOrphansFor(NodeId peer);
|
||||
|
||||
static void CheckBlockIndex();
|
||||
|
||||
/** Constant stuff for coinbase transactions we create: */
|
||||
CScript COINBASE_FLAGS;
|
||||
|
||||
@ -83,7 +86,7 @@ namespace {
|
||||
|
||||
struct CBlockIndexWorkComparator
|
||||
{
|
||||
bool operator()(CBlockIndex *pa, CBlockIndex *pb) {
|
||||
bool operator()(CBlockIndex *pa, CBlockIndex *pb) const {
|
||||
// First sort by most total work, ...
|
||||
if (pa->nChainWork > pb->nChainWork) return false;
|
||||
if (pa->nChainWork < pb->nChainWork) return true;
|
||||
@ -105,8 +108,8 @@ namespace {
|
||||
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.
|
||||
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
|
||||
* as good as our current tip or better. Entries may be failed, though.
|
||||
*/
|
||||
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
|
||||
/** Number of nodes with fSyncStarted. */
|
||||
@ -2605,6 +2608,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
|
||||
uiInterface.NotifyBlockTip(hashNewTip);
|
||||
}
|
||||
} while(pindexMostWork != chainActive.Tip());
|
||||
CheckBlockIndex();
|
||||
|
||||
// Write changes periodically to disk, after relay.
|
||||
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
|
||||
@ -2741,7 +2745,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
|
||||
CBlockIndex *pindex = queue.front();
|
||||
queue.pop_front();
|
||||
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
|
||||
setBlockIndexCandidates.insert(pindex);
|
||||
if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) {
|
||||
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;
|
||||
@ -3267,6 +3273,7 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis
|
||||
if (pindex && pfrom) {
|
||||
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
|
||||
}
|
||||
CheckBlockIndex();
|
||||
if (!ret)
|
||||
return error("%s : AcceptBlock FAILED", __func__);
|
||||
}
|
||||
@ -3743,6 +3750,136 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
|
||||
return nLoaded > 0;
|
||||
}
|
||||
|
||||
void static CheckBlockIndex()
|
||||
{
|
||||
if (!fCheckBlockIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
// Build forward-pointing map of the entire block tree.
|
||||
std::multimap<CBlockIndex*,CBlockIndex*> forward;
|
||||
for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
|
||||
forward.insert(std::make_pair(it->second->pprev, it->second));
|
||||
}
|
||||
|
||||
assert(forward.size() == mapBlockIndex.size());
|
||||
|
||||
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(NULL);
|
||||
CBlockIndex *pindex = rangeGenesis.first->second;
|
||||
rangeGenesis.first++;
|
||||
assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL.
|
||||
|
||||
// Iterate over the entire block tree, using depth-first search.
|
||||
// Along the way, remember whether there are blocks on the path from genesis
|
||||
// block being explored which are the first to have certain properties.
|
||||
size_t nNodes = 0;
|
||||
int nHeight = 0;
|
||||
CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid.
|
||||
CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA.
|
||||
CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not).
|
||||
CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not).
|
||||
CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not).
|
||||
while (pindex != NULL) {
|
||||
nNodes++;
|
||||
if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
|
||||
if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex;
|
||||
if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex;
|
||||
if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex;
|
||||
if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex;
|
||||
|
||||
// Begin: actual consistency checks.
|
||||
if (pindex->pprev == NULL) {
|
||||
// Genesis block checks.
|
||||
assert(pindex->GetBlockHash() == Params().HashGenesisBlock()); // Genesis block's hash must match.
|
||||
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
|
||||
}
|
||||
assert((pindexFirstMissing != NULL) == (pindex->nChainTx == 0)); // nChainTx == 0 is used to signal that all parent block's transaction data is available.
|
||||
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
|
||||
assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
|
||||
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
|
||||
assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid
|
||||
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid
|
||||
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid
|
||||
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid
|
||||
if (pindexFirstInvalid == NULL) {
|
||||
// Checks for not-invalid blocks.
|
||||
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
|
||||
}
|
||||
if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstMissing == NULL) {
|
||||
if (pindexFirstInvalid == NULL) { // If this block sorts at least as good as the current tip and is valid, it must be in setBlockIndexCandidates.
|
||||
assert(setBlockIndexCandidates.count(pindex));
|
||||
}
|
||||
} else { // If this block sorts worse than the current tip, it cannot be in setBlockIndexCandidates.
|
||||
assert(setBlockIndexCandidates.count(pindex) == 0);
|
||||
}
|
||||
// Check whether this block is in mapBlocksUnlinked.
|
||||
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev);
|
||||
bool foundInUnlinked = false;
|
||||
while (rangeUnlinked.first != rangeUnlinked.second) {
|
||||
assert(rangeUnlinked.first->first == pindex->pprev);
|
||||
if (rangeUnlinked.first->second == pindex) {
|
||||
foundInUnlinked = true;
|
||||
break;
|
||||
}
|
||||
rangeUnlinked.first++;
|
||||
}
|
||||
if (pindex->pprev && pindex->nStatus & BLOCK_HAVE_DATA && pindexFirstMissing != NULL) {
|
||||
if (pindexFirstInvalid == NULL) { // If this block has block data available, some parent doesn't, and has no invalid parents, it must be in mapBlocksUnlinked.
|
||||
assert(foundInUnlinked);
|
||||
}
|
||||
} else { // If this block does not have block data available, or all parents do, it cannot be in mapBlocksUnlinked.
|
||||
assert(!foundInUnlinked);
|
||||
}
|
||||
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
|
||||
// End: actual consistency checks.
|
||||
|
||||
// Try descending into the first subnode.
|
||||
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> range = forward.equal_range(pindex);
|
||||
if (range.first != range.second) {
|
||||
// A subnode was found.
|
||||
pindex = range.first->second;
|
||||
nHeight++;
|
||||
continue;
|
||||
}
|
||||
// This is a leaf node.
|
||||
// Move upwards until we reach a node of which we have not yet visited the last child.
|
||||
while (pindex) {
|
||||
// We are going to either move to a parent or a sibling of pindex.
|
||||
// If pindex was the first with a certain property, unset the corresponding variable.
|
||||
if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL;
|
||||
if (pindex == pindexFirstMissing) pindexFirstMissing = NULL;
|
||||
if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL;
|
||||
if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL;
|
||||
if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL;
|
||||
// Find our parent.
|
||||
CBlockIndex* pindexPar = pindex->pprev;
|
||||
// Find which child we just visited.
|
||||
std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangePar = forward.equal_range(pindexPar);
|
||||
while (rangePar.first->second != pindex) {
|
||||
assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child.
|
||||
rangePar.first++;
|
||||
}
|
||||
// Proceed to the next one.
|
||||
rangePar.first++;
|
||||
if (rangePar.first != rangePar.second) {
|
||||
// Move to the sibling.
|
||||
pindex = rangePar.first->second;
|
||||
break;
|
||||
} else {
|
||||
// Move up further.
|
||||
pindex = pindexPar;
|
||||
nHeight--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we actually traversed the entire map.
|
||||
assert(nNodes == forward.size());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CAlert
|
||||
@ -4617,6 +4754,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||
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));
|
||||
}
|
||||
|
||||
CheckBlockIndex();
|
||||
}
|
||||
|
||||
else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing
|
||||
|
@ -127,6 +127,7 @@ extern bool fReindex;
|
||||
extern int nScriptCheckThreads;
|
||||
extern bool fTxIndex;
|
||||
extern bool fIsBareMultisigStd;
|
||||
extern bool fCheckBlockIndex;
|
||||
extern unsigned int nCoinCacheSize;
|
||||
extern CFeeRate minRelayTxFee;
|
||||
|
||||
|
@ -1285,8 +1285,7 @@ void ThreadOpenConnections()
|
||||
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);
|
||||
CAddress addr = addrman.Select();
|
||||
|
||||
// if we selected an invalid address, restart
|
||||
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
|
||||
|
102
src/test/mempool_tests.cpp
Normal file
102
src/test/mempool_tests.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// 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 "main.h"
|
||||
#include "txmempool.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <list>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(mempool_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
|
||||
{
|
||||
// Test CTxMemPool::remove functionality
|
||||
|
||||
// Parent transaction with three children,
|
||||
// and three grand-children:
|
||||
CMutableTransaction txParent;
|
||||
txParent.vin.resize(1);
|
||||
txParent.vin[0].scriptSig = CScript() << OP_11;
|
||||
txParent.vout.resize(3);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||
txParent.vout[i].nValue = 33000LL;
|
||||
}
|
||||
CMutableTransaction txChild[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
txChild[i].vin.resize(1);
|
||||
txChild[i].vin[0].scriptSig = CScript() << OP_11;
|
||||
txChild[i].vin[0].prevout.hash = txParent.GetHash();
|
||||
txChild[i].vin[0].prevout.n = i;
|
||||
txChild[i].vout.resize(1);
|
||||
txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||
txChild[i].vout[0].nValue = 11000LL;
|
||||
}
|
||||
CMutableTransaction txGrandChild[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
txGrandChild[i].vin.resize(1);
|
||||
txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
|
||||
txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
|
||||
txGrandChild[i].vin[0].prevout.n = 0;
|
||||
txGrandChild[i].vout.resize(1);
|
||||
txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||
txGrandChild[i].vout[0].nValue = 11000LL;
|
||||
}
|
||||
|
||||
|
||||
CTxMemPool testPool(CFeeRate(0));
|
||||
std::list<CTransaction> removed;
|
||||
|
||||
// Nothing in pool, remove should do nothing:
|
||||
testPool.remove(txParent, removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 0);
|
||||
|
||||
// Just the parent:
|
||||
testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1));
|
||||
testPool.remove(txParent, removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 1);
|
||||
removed.clear();
|
||||
|
||||
// Parent, children, grandchildren:
|
||||
testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1));
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1));
|
||||
testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1));
|
||||
}
|
||||
// Remove Child[0], GrandChild[0] should be removed:
|
||||
testPool.remove(txChild[0], removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 2);
|
||||
removed.clear();
|
||||
// ... make sure grandchild and child are gone:
|
||||
testPool.remove(txGrandChild[0], removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 0);
|
||||
testPool.remove(txChild[0], removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 0);
|
||||
// Remove parent, all children/grandchildren should go:
|
||||
testPool.remove(txParent, removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 5);
|
||||
BOOST_CHECK_EQUAL(testPool.size(), 0);
|
||||
removed.clear();
|
||||
|
||||
// Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1));
|
||||
testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1));
|
||||
}
|
||||
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
|
||||
// put into the mempool (maybe because it is non-standard):
|
||||
testPool.remove(txParent, removed, true);
|
||||
BOOST_CHECK_EQUAL(removed.size(), 6);
|
||||
BOOST_CHECK_EQUAL(testPool.size(), 0);
|
||||
removed.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -31,6 +31,7 @@ struct TestingSetup {
|
||||
|
||||
TestingSetup() {
|
||||
fPrintToDebugLog = false; // don't want to write to debug.log file
|
||||
fCheckBlockIndex = true;
|
||||
SelectParams(CBaseChainParams::UNITTEST);
|
||||
noui_connect();
|
||||
#ifdef ENABLE_WALLET
|
||||
|
@ -444,6 +444,18 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
||||
LOCK(cs);
|
||||
std::deque<uint256> txToRemove;
|
||||
txToRemove.push_back(origTx.GetHash());
|
||||
if (fRecursive && !mapTx.count(origTx.GetHash())) {
|
||||
// If recursively removing but origTx isn't in the mempool
|
||||
// be sure to remove any children that are in the pool. This can
|
||||
// happen during chain re-orgs if origTx isn't re-accepted into
|
||||
// the mempool for any reason.
|
||||
for (unsigned int i = 0; i < origTx.vout.size(); i++) {
|
||||
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(origTx.GetHash(), i));
|
||||
if (it == mapNextTx.end())
|
||||
continue;
|
||||
txToRemove.push_back(it->second.ptx->GetHash());
|
||||
}
|
||||
}
|
||||
while (!txToRemove.empty())
|
||||
{
|
||||
uint256 hash = txToRemove.front();
|
||||
|
Loading…
Reference in New Issue
Block a user