diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 7952de663..d0229b7b1 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -1,752 +1,767 @@ Developer Notes =============== Various coding styles have been used during the history of the codebase, and the result is not very consistent. However, we're now trying to converge to a single style, so please use it in new code. Old code will be converted gradually and you are encouraged to use the provided [clang-format-diff script](/contrib/devtools/README.md#clang-format-diffpy) to clean up the patch automatically before submitting a pull request. - Basic rules specified in [src/.clang-format](/src/.clang-format). - Braces on new lines for namespaces, classes, functions, methods. - Braces on the same line for everything else. - 4 space indentation (no tabs) for every block except namespaces. - No indentation for `public`/`protected`/`private` or for `namespace`. - No extra spaces inside parenthesis; don't do ( this ) - No space after function names; one space after `if`, `for` and `while`. - Always add braces for block statements (e.g. `if`, `for`, `while`). - `++i` is preferred over `i++`. - `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking. - Use CamelCase for functions/methods, and lowerCamelCase for variables. - GLOBAL_CONSTANTS should use UPPER_SNAKE_CASE. - namespaces should use lower_snake_case. - Function names should generally start with an English command-form verb (e.g. `ValidateTransaction`, `AddTransactionToMempool`, `ConnectBlock`) - Variable names should generally be nouns or past/future tense verbs. (e.g. `canDoThing`, `signatureOperations`, `didThing`) - Avoid using globals, remove existing globals whenever possible. - Class member variable names should be prepended with `m_` - DO choose easily readable identifier names. - DO favor readability over brevity. - DO NOT use Hungarian notation. - DO NOT use abbreviations or contractions within identifiers. - WRONG: mempool - RIGHT: MemoryPool - WRONG: ChangeDir - RIGHT: ChangeDirectory - DO NOT use obscure acronyms, DO uppercase any acronyms. - FINALLY, do not migrate existing code unless refactoring. It makes forwarding-porting from Bitcoin Core more difficult. The naming convention roughly mirrors [Microsoft Naming Conventions](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/general-naming-conventions) C++ Coding Standards should strive to follow the [LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html) Code style example: ```c++ // namespaces should be lower_snake_case namespace foo_bar_bob { /** * Class is used for doing classy things. All classes should * have a doxygen comment describing their PURPOSE. That is to say, * why they exist. Functional details can be determined from the code. * @see PerformTask() */ class Class { private: //! memberVariable's name should be lowerCamelCase, and be a noun. int m_memberVariable; public: /** * The documentation before a function or class method should follow Doxygen * spec. The name of the function should start with an english verb which * indicates the intended purpose of this code. * * The function name should be should be CamelCase. * * @param[in] s A description * @param[in] n Another argument description * @pre Precondition for function... */ bool PerformTask(const std::string& s, int n) { // Use lowerChamelCase for local variables. bool didMore = false; // Comment summarizing the intended purpose of this section of code for (int i = 0; i < n; ++i) { if (!DidSomethingFail()) { return false; } ... if (IsSomethingElse()) { DoMore(); didMore = true; } else { DoLess(); } } return didMore; } } -} +} // namespace foo ``` Doxygen comments ----------------- To facilitate the generation of documentation, use doxygen-compatible comment blocks for functions, methods and fields. For example, to describe a function use: ```c++ /** * ... text ... * @param[in] arg1 A description * @param[in] arg2 Another argument description * @pre Precondition for function... */ bool function(int arg1, const char *arg2) ``` A complete list of `@xxx` commands can be found at http://www.stack.nl/~dimitri/doxygen/manual/commands.html. As Doxygen recognizes the comments by the delimiters (`/**` and `*/` in this case), you don't *need* to provide any commands for a comment to be valid; just a description text is fine. To describe a class use the same construct above the class definition: ```c++ /** * Alerts are for notifying old versions if they become too obsolete and * need to upgrade. The message is displayed in the status bar. * @see GetWarnings() */ class CAlert { ``` To describe a member or variable use: ```c++ int var; //!< Detailed description after the member ``` or ```cpp //! Description before the member int var; ``` Also OK: ```c++ /// /// ... text ... /// bool function2(int arg1, const char *arg2) ``` Not OK (used plenty in the current source, but not picked up): ```c++ // // ... text ... // ``` A full list of comment syntaxes picked up by doxygen can be found at http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html, but if possible use one of the above styles. To build doxygen locally to test changes to the Doxyfile or visualize your comments before landing changes: ``` # In the build directory, call: doxygen doc/Doxyfile # output goes to doc/doxygen/html/ ``` Development tips and tricks --------------------------- **compiling for debugging** Run configure with the --enable-debug option, then make. Or run configure with CXXFLAGS="-g -ggdb -O0" or whatever debug flags you need. **debug.log** If the code is behaving strangely, take a look in the debug.log file in the data directory; error and debugging messages are written there. The -debug=... command-line option controls debugging; running with just -debug or -debug=1 will turn on all categories (and give you a very large debug.log file). The Qt code routes qDebug() output to debug.log under category "qt": run with -debug=qt to see it. **writing tests** For details on unit tests, see `unit-tests.md` For details on functional tests, see `functional-tests.md` **writing script integration tests** Script integration tests are built using `src/test/script_tests.cpp`: 1. Uncomment the line with `#define UPDATE_JSON_TESTS` 2. Add a new TestBuilder to the `script_build` test to cover your test case. 3. `make && ./src/test/test_bitcoin --run_test=script_tests` 4. Copy your newly generated test JSON from `/src/script_tests.json.gen` to `src/test/data/script_tests.json`. Please commit your TestBuilder along with your generated test JSON and cleanup the uncommented #define before code review. **testnet and regtest modes** Run with the -testnet option to run with "play bitcoins" on the test network, if you are testing multi-machine code that needs to operate across the internet. If you are testing something that can run on one machine, run with the -regtest option. In regression test mode, blocks can be created on-demand; see test/functional/ for tests that run in -regtest mode. **DEBUG_LOCKORDER** Bitcoin Core is a multithreaded application, and deadlocks or other multithreading bugs can be very difficult to track down. Compiling with -DDEBUG_LOCKORDER (configure CXXFLAGS="-DDEBUG_LOCKORDER -g") inserts run-time checks to keep track of which locks are held, and adds warnings to the debug.log file if inconsistencies are detected. **Sanitizers** Bitcoin can be compiled with various "sanitizers" enabled, which add instrumentation for issues regarding things like memory safety, thread race conditions, or undefined behavior. This is controlled with the `--with-sanitizers` configure flag, which should be a comma separated list of sanitizers to enable. The sanitizer list should correspond to supported `-fsanitize=` options in your compiler. These sanitizers have runtime overhead, so they are most useful when testing changes or producing debugging builds. Some examples: ```bash # Enable both the address sanitizer and the undefined behavior sanitizer ./configure --with-sanitizers=address,undefined # Enable the thread sanitizer ./configure --with-sanitizers=thread ``` If you are compiling with GCC you will typically need to install corresponding "san" libraries to actually compile with these flags, e.g. libasan for the address sanitizer, libtsan for the thread sanitizer, and libubsan for the undefined sanitizer. If you are missing required libraries, the configure script will fail with a linker error when testing the sanitizer flags. The test suite should pass cleanly with the `thread` and `undefined` sanitizers, but there are a number of known problems when using the `address` sanitizer. The address sanitizer is known to fail in [sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable unless you also use `--disable-asm` when running configure. We would like to fix sanitizer issues, so please send pull requests if you can fix any errors found by the address sanitizer (or any other sanitizer). Not all sanitizer options can be enabled at the same time, e.g. trying to build with `--with-sanitizers=address,thread` will fail in the configure script as these sanitizers are mutually incompatible. Refer to your compiler manual to learn more about these options and which sanitizers are supported by your compiler. Additional resources: * [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) * [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html) * [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html) * [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) * [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) * [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html) * [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki) * [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691) Locking/mutex usage notes ------------------------- The code is multi-threaded, and uses mutexes and the LOCK/TRY_LOCK macros to protect data structures. Deadlocks due to inconsistent lock ordering (thread 1 locks cs_main and then cs_wallet, while thread 2 locks them in the opposite order: result, deadlock as each waits for the other to release its lock) are a problem. Compile with -DDEBUG_LOCKORDER to get lock order inconsistencies reported in the debug.log file. Re-architecting the core code so there are better-defined interfaces between the various components is a goal, with any necessary locking done by the components (e.g. see the self-contained CKeyStore class and its cs_KeyStore lock for example). Threads ------- - ThreadScriptCheck : Verifies block scripts. - ThreadImport : Loads blocks from blk*.dat files or bootstrap.dat. - StartNode : Starts other threads. - ThreadDNSAddressSeed : Loads addresses of peers from the DNS. - ThreadMapPort : Universal plug-and-play startup/shutdown - ThreadSocketHandler : Sends/Receives data from peers on port 8333. - ThreadOpenAddedConnections : Opens network connections to added nodes. - ThreadOpenConnections : Initiates new connections to peers. - ThreadMessageHandler : Higher-level message handling (sending and receiving). - DumpAddresses : Dumps IP addresses of nodes to peers.dat. - ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them. - Shutdown : Does an orderly shutdown of everything. Ignoring IDE/editor files -------------------------- In closed-source environments in which everyone uses the same IDE it is common to add temporary files it produces to the project-wide `.gitignore` file. However, in open source software such as Bitcoin Core, where everyone uses their own editors/IDE/tools, it is less common. Only you know what files your editor produces and this may change from version to version. The canonical way to do this is thus to create your local gitignore. Add this to `~/.gitconfig`: ``` [core] excludesfile = /home/.../.gitignore_global ``` (alternatively, type the command `git config --global core.excludesfile ~/.gitignore_global` on a terminal) Then put your favourite tool's temporary filenames in that file, e.g. ``` # NetBeans nbproject/ ``` Another option is to create a per-repository excludes file `.git/info/exclude`. These are not committed but apply only to one repository. If a set of tools is used by the build system or scripts the repository (for example, lcov) it is perfectly acceptable to add its files to `.gitignore` and commit them. Development guidelines ============================ A few non-style-related recommendations for developers, as well as points to pay attention to for reviewers of Bitcoin Core code. Wallet ------- - Make sure that no crashes happen with run-time option `-disablewallet`. - *Rationale*: In RPC code that conditionally uses the wallet (such as `validateaddress`) it is easy to forget that global pointer `pwalletMain` can be NULL. See `test/functional/disablewallet.py` for functional tests exercising the API with `-disablewallet` - Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set - *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB General C++ ------------- - Assertions should not have side-effects - *Rationale*: Even though the source code is set to refuse to compile with assertions disabled, having side-effects in assertions is unexpected and makes the code harder to understand - If you use the `.h`, you must link the `.cpp` - *Rationale*: Include files define the interface for the code in implementation files. Including one but not linking the other is confusing. Please avoid that. Moving functions from the `.h` to the `.cpp` should not result in build errors - Use the RAII (Resource Acquisition Is Initialization) paradigm where possible. For example by using `unique_ptr` for allocations in a function. - *Rationale*: This avoids memory and resource leaks, and ensures exception safety C++ data structures -------------------- - Never use the `std::map []` syntax when reading from a map, but instead use `.find()` - *Rationale*: `[]` does an insert (of the default element) if the item doesn't exist in the map yet. This has resulted in memory leaks in the past, as well as race conditions (expecting read-read behavior). Using `[]` is fine for *writing* to a map - Do not compare an iterator from one data structure with an iterator of another data structure (even if of the same type) - *Rationale*: Behavior is undefined. In C++ parlor this means "may reformat the universe", in practice this has resulted in at least one hard-to-debug crash bug - Watch out for out-of-bounds vector access. `&vch[vch.size()]` is illegal, including `&vch[0]` for an empty vector. Use `vch.data()` and `vch.data() + vch.size()` instead. - Vector bounds checking is only enabled in debug mode. Do not rely on it - Make sure that constructors initialize all fields. If this is skipped for a good reason (i.e., optimization on the critical path), add an explicit comment about this - *Rationale*: Ensure determinism by avoiding accidental use of uninitialized values. Also, static analyzers balk about this. - Use explicitly signed or unsigned `char`s, or even better `uint8_t` and `int8_t`. Do not use bare `char` unless it is to pass to a third-party API. This type can be signed or unsigned depending on the architecture, which can lead to interoperability problems or dangerous conditions such as out-of-bounds array accesses - Prefer explicit constructions over implicit ones that rely on 'magical' C++ behavior - *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those that are not language lawyers - Initialize all non-static class members where they are defined - *Rationale*: Initializing the members in the declaration makes it easy to spot uninitialized ones, and avoids accidentally reading uninitialized memory ```cpp class A { uint32_t m_count{0}; } ``` Strings and formatting ------------------------ - Use `std::string`, avoid C string manipulation functions - *Rationale*: C++ string handling is marginally safer, less scope for buffer overflows and surprises with `\0` characters. Also some C string manipulations tend to act differently depending on platform, or even the user locale - Use `ParseInt32`, `ParseInt64`, `ParseUInt32`, `ParseUInt64`, `ParseDouble` from `utilstrencodings.h` for number parsing - *Rationale*: These functions do overflow checking, and avoid pesky locale issues Variable names -------------- The shadowing warning (`-Wshadow`) is enabled by default. It prevents issues rising from using a different variable with the same name. E.g. in member initializers, prepend `_` to the argument name shadowing the member name: ```c++ class AddressBookPage { Mode m_mode; } AddressBookPage::AddressBookPage(Mode _mode) : m_mode(_mode) ... ``` When using nested cycles, do not name the inner cycle variable the same as in upper cycle etc. Please name variables so that their names do not shadow variables defined in the source code. Threads and synchronization ---------------------------- - Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential deadlocks are introduced. As of 0.12, this is defined by default when configuring with `--enable-debug` - When using `LOCK`/`TRY_LOCK` be aware that the lock exists in the context of the current scope, so surround the statement and the code that needs the lock with braces OK: ```c++ { TRY_LOCK(cs_vNodes, lockNodes); ... } ``` Wrong: ```c++ TRY_LOCK(cs_vNodes, lockNodes); { ... } ``` Source code organization -------------------------- - Implementation code should go into the `.cpp` file and not the `.h`, unless necessary due to template usage or when performance due to inlining is critical - *Rationale*: Shorter and simpler header files are easier to read, and reduce compile time - Use only the lowercase alphanumerics (`a-z0-9`), underscore (`_`) and hyphen (`-`) in source code filenames. - *Rationale*: `grep`:ing and auto-completing filenames is easier when using a consistent naming pattern. Potential problems when building on case-insensitive filesystems are avoided when using only lowercase characters in source code filenames. - Don't import anything into the global namespace (`using namespace ...`). Use fully specified types such as `std::string`. - *Rationale*: Avoids symbol conflicts +- Terminate namespaces with a comment (`// namespace mynamespace`). The comment + should be placed on the same line as the brace closing the namespace, e.g. + +```c++ +namespace mynamespace { + ... +} // namespace mynamespace + +namespace { + ... +} // namespace +``` + + - *Rationale*: Avoids confusion about the namespace context + Header Inclusions ----------------- - Header inclusions should use angle brackets (`#include <>`). The include path should be relative to the `src` folder. e.g.: `#include ` - Native C++ headers should be preferred over C compatibility headers. e.g.: use `` instead of `` - In order to make the code consistent, header files should be included in the following order, with each section separated by a newline: 1. In a .cpp file, the associated .h is in first position. In a test source, this is the header file under test. 2. The project headers. 3. The test headers. 4. The 3rd party libraries headers. Different libraries should be in different sections. 5. The system libraries. All headers should be lexically ordered inside their block. - Use include guards to avoid the problem of double inclusion. The header file `foo/bar.h` should use the include guard identifier `BITCOIN_FOO_BAR_H`, e.g. ```c++ #ifndef BITCOIN_FOO_BAR_H #define BITCOIN_FOO_BAR_H ... #endif // BITCOIN_FOO_BAR_H ``` GUI ----- - Do not display or manipulate dialogs in model code (classes `*Model`) - *Rationale*: Model classes pass through events and data from the core, they should not interact with the user. That's where View classes come in. The converse also holds: try to not directly access core data structures from Views. - Avoid adding slow or blocking code in the GUI thread. In particular do not add new `interface::Node` and `interface::Wallet` method calls, even if they may be fast now, in case they are changed to lock or communicate across processes in the future. Prefer to offload work from the GUI thread to worker threads (see `RPCExecutor` in console code as an example) or take other steps (see https://doc.qt.io/archives/qq/qq27-responsive-guis.html) to keep the GUI responsive. - *Rationale*: Blocking the GUI thread can increase latency, and lead to hangs and deadlocks. Unit Tests ----------- - Test suite naming convention: The Boost test suite in file `src/test/foo_tests.cpp` should be named `foo_tests`. Test suite names must be unique. Subtrees ---------- Several parts of the repository are subtrees of software maintained elsewhere. Some of these are maintained by active developers of Bitcoin Core, in which case changes should probably go directly upstream without being PRed directly against the project. They will be merged back in the next subtree merge. Others are external projects without a tight relationship with our project. Changes to these should also be sent upstream but bugfixes may also be prudent to PR against Bitcoin Core so that they can be integrated quickly. Cosmetic changes should be purely taken upstream. There is a tool in contrib/devtools/git-subtree-check.sh to check a subtree directory for consistency with its upstream repository. Current subtrees include: - src/leveldb - Upstream at https://github.com/google/leveldb ; Maintained by Google, but open important PRs to Core to avoid delay - src/libsecp256k1 - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintaned by Core contributors. - src/crypto/ctaes - Upstream at https://github.com/bitcoin-core/ctaes ; actively maintained by Core contributors. - src/univalue - Upstream at https://github.com/jgarzik/univalue ; report important PRs to Core to avoid delay. Git and GitHub tips --------------------- - Github is not typically the source of truth for pull requests. See CONTRIBUTING.md for instructions on setting up your repo correctly. - Similarly, your git remote origin should be set to: `ssh://vcs@reviews.bitcoinabc.org:2221/source/bitcoin-abc.git` instead of github.com. See CONTRIBUTING.md for details. - For resolving merge/rebase conflicts, it can be useful to enable diff3 style using `git config merge.conflictstyle diff3`. Instead of <<< yours === theirs >>> you will see <<< yours ||| original === theirs >>> This may make it much clearer what caused the conflict. In this style, you can often just look at what changed between *original* and *theirs*, and mechanically apply that to *yours* (or the other way around). - When reviewing patches which change indentation in C++ files, use `git diff -w` and `git show -w`. This makes the diff algorithm ignore whitespace changes. This feature is also available on github.com, by adding `?w=1` at the end of any URL which shows a diff. - When reviewing patches that change symbol names in many places, use `git diff --word-diff`. This will instead of showing the patch as deleted/added *lines*, show deleted/added *words*. - When reviewing patches that move code around, try using `git diff --patience commit~:old/file.cpp commit:new/file/name.cpp`, and ignoring everything except the moved body of code which should show up as neither `+` or `-` lines. In case it was not a pure move, this may even work when combined with the `-w` or `--word-diff` options described above. - When looking at other's pull requests, it may make sense to add the following section to your `.git/config` file: [remote "upstream-pull"] fetch = +refs/pull/*:refs/remotes/upstream-pull/* url = git@github.com:bitcoin/bitcoin.git This will add an `upstream-pull` remote to your git repository, which can be fetched using `git fetch --all` or `git fetch upstream-pull`. Afterwards, you can use `upstream-pull/NUMBER/head` in arguments to `git show`, `git checkout` and anywhere a commit id would be acceptable to see the changes from pull request NUMBER. RPC interface guidelines -------------------------- A few guidelines for introducing and reviewing new RPC interfaces: - Method naming: use consecutive lower-case names such as `getrawtransaction` and `submitblock` - *Rationale*: Consistency with existing interface. - Argument naming: use snake case `fee_delta` (and not, e.g. camel case `feeDelta`) - *Rationale*: Consistency with existing interface. - Use the JSON parser for parsing, don't manually parse integers or strings from arguments unless absolutely necessary. - *Rationale*: Introduces hand-rolled string manipulation code at both the caller and callee sites, which is error prone, and it is easy to get things such as escaping wrong. JSON already supports nested data structures, no need to re-invent the wheel. - *Exception*: AmountFromValue can parse amounts as string. This was introduced because many JSON parsers and formatters hard-code handling decimal numbers as floating point values, resulting in potential loss of precision. This is unacceptable for monetary values. **Always** use `AmountFromValue` and `ValueFromAmount` when inputting or outputting monetary values. The only exceptions to this are `prioritisetransaction` and `getblocktemplate` because their interface is specified as-is in BIP22. - Missing arguments and 'null' should be treated the same: as default values. If there is no default value, both cases should fail in the same way. The easiest way to follow this guideline is detect unspecified arguments with `params[x].isNull()` instead of `params.size() <= x`. The former returns true if the argument is either null or missing, while the latter returns true if is missing, and false if it is null. - *Rationale*: Avoids surprises when switching to name-based arguments. Missing name-based arguments are passed as 'null'. - Try not to overload methods on argument type. E.g. don't make `getblock(true)` and `getblock("hash")` do different things. - *Rationale*: This is impossible to use with `bitcoin-cli`, and can be surprising to users. - *Exception*: Some RPC calls can take both an `int` and `bool`, most notably when a bool was switched to a multi-value, or due to other historical reasons. **Always** have false map to 0 and true to 1 in this case. - Don't forget to fill in the argument names correctly in the RPC command table. - *Rationale*: If not, the call can not be used with name-based arguments. - Set okSafeMode in the RPC command table to a sensible value: safe mode is when the blockchain is regarded to be in a confused state, and the client deems it unsafe to do anything irreversible such as send. Anything that just queries should be permitted. - *Rationale*: Troubleshooting a node in safe mode is difficult if half the RPCs don't work. - Add every non-string RPC argument `(method, idx, name)` to the table `vRPCConvertParams` in `rpc/client.cpp`. - *Rationale*: `bitcoin-cli` and the GUI debug console use this table to determine how to convert a plaintext command line to JSON. If the types don't match, the method can be unusable from there. - A RPC method must either be a wallet method or a non-wallet method. Do not introduce new methods such as `signrawtransaction` that differ in behavior based on presence of a wallet. - *Rationale*: as well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be separated to avoid introducing circular dependencies between code units. - Try to make the RPC response a JSON object. - *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if new data in the response is needed. - Wallet RPCs call BlockUntilSyncedToCurrentChain to maintain consistency with `getblockchaininfo`'s state immediately prior to the call's execution. Wallet RPCs whose behavior does *not* depend on the current chainstate may omit this call. - *Rationale*: In previous versions of Bitcoin Core, the wallet was always in-sync with the chainstate (by virtue of them all being updated in the same cs_main lock). In order to maintain the behavior that wallet RPCs return results as of at least the highest best-known block an RPC client may be aware of prior to entering a wallet RPC call, we must block until the wallet is caught up to the chainstate as of the RPC call's entry. This also makes the API much easier for RPC clients to reason about. diff --git a/src/.clang-format b/src/.clang-format index f6c430223..1e71b84f3 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -1,18 +1,18 @@ Language: Cpp Standard: Cpp11 ColumnLimit: 80 IndentWidth: 4 TabWidth: 4 AccessModifierOffset: -4 UseTab: Never IndentCaseLabels: true NamespaceIndentation: Inner BreakBeforeBraces: Attach ReflowComments: true AlignEscapedNewlines: Right AllowShortBlocksOnASingleLine: false AllowShortIfStatementsOnASingleLine: true AllowShortFunctionsOnASingleLine: Inline -FixNamespaceComments: false +FixNamespaceComments: true ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, BOOST_REVERSE_FOREACH ] CommentPragmas: '@DISABLE FORMATING FOR THIS COMMENT@' diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 855963dcb..dc6ab48b1 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -1,148 +1,148 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 #include #include #include #include #include #include #include #include #include namespace { template bool SerializeDB(const CChainParams &chainParams, Stream &stream, const Data &data) { // Write and commit header, data try { CHashWriter hasher(SER_DISK, CLIENT_VERSION); stream << FLATDATA(chainParams.DiskMagic()) << data; hasher << FLATDATA(chainParams.DiskMagic()) << data; stream << hasher.GetHash(); } catch (const std::exception &e) { return error("%s: Serialize or I/O error - %s", __func__, e.what()); } return true; } template bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix, const fs::path &path, const Data &data) { // Generate random temporary filename unsigned short randv = 0; GetRandBytes((uint8_t *)&randv, sizeof(randv)); std::string tmpfn = strprintf("%s.%04x", prefix, randv); // open temp output file, and associate with CAutoFile fs::path pathTmp = GetDataDir() / tmpfn; FILE *file = fsbridge::fopen(pathTmp, "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) { return error("%s: Failed to open file %s", __func__, pathTmp.string()); } // Serialize if (!SerializeDB(chainParams, fileout, data)) { return false; } if (!FileCommit(fileout.Get())) { return error("%s: Failed to flush file %s", __func__, pathTmp.string()); } fileout.fclose(); // replace existing file, if any, with new file if (!RenameOver(pathTmp, path)) { return error("%s: Rename-into-place failed", __func__); } return true; } template bool DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data, bool fCheckSum = true) { try { CHashVerifier verifier(&stream); // de-serialize file header (network specific magic number) and .. uint8_t pchMsgTmp[4]; verifier >> FLATDATA(pchMsgTmp); // ... verify the network matches ours if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()), sizeof(pchMsgTmp))) { return error("%s: Invalid network magic number", __func__); } // de-serialize data verifier >> data; // verify checksum if (fCheckSum) { uint256 hashTmp; stream >> hashTmp; if (hashTmp != verifier.GetHash()) { return error("%s: Checksum mismatch, data corrupted", __func__); } } } catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } return true; } template bool DeserializeFileDB(const CChainParams &chainParams, const fs::path &path, Data &data) { // open input file, and associate with CAutoFile FILE *file = fsbridge::fopen(path, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { return error("%s: Failed to open file %s", __func__, path.string()); } return DeserializeDB(chainParams, filein, data); } -} +} // namespace CBanDB::CBanDB(const CChainParams &chainParamsIn) : chainParams(chainParamsIn) { pathBanlist = GetDataDir() / "banlist.dat"; } bool CBanDB::Write(const banmap_t &banSet) { return SerializeFileDB(chainParams, "banlist", pathBanlist, banSet); } bool CBanDB::Read(banmap_t &banSet) { return DeserializeFileDB(chainParams, pathBanlist, banSet); } CAddrDB::CAddrDB(const CChainParams &chainParamsIn) : chainParams(chainParamsIn) { pathAddr = GetDataDir() / "peers.dat"; } bool CAddrDB::Write(const CAddrMan &addr) { return SerializeFileDB(chainParams, "peers", pathAddr, addr); } bool CAddrDB::Read(CAddrMan &addr) { return DeserializeFileDB(chainParams, pathAddr, addr); } bool CAddrDB::Read(CAddrMan &addr, CDataStream &ssPeers) { bool ret = DeserializeDB(chainParams, ssPeers, addr, false); if (!ret) { // Ensure addrman is left in a clean state addr.Clear(); } return ret; } diff --git a/src/avalanche.h b/src/avalanche.h index 3c8cbe536..bed0467a5 100644 --- a/src/avalanche.h +++ b/src/avalanche.h @@ -1,354 +1,354 @@ // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_AVALANCHE_H #define BITCOIN_AVALANCHE_H #include #include #include // for CInv #include #include #include #include #include #include #include #include #include #include #include #include #include class Config; class CBlockIndex; class CScheduler; namespace { /** * Finalization score. */ static const int AVALANCHE_FINALIZATION_SCORE = 128; /** * How long before we consider that a query timed out. */ static const int AVALANCHE_DEFAULT_QUERY_TIMEOUT_DURATION_MILLISECONDS = 10000; /** * How many inflight requests can exist for one item. */ static const int AVALANCHE_MAX_INFLIGHT_POLL = 10; /** * Special NodeId that represent no node. */ static const NodeId NO_NODE = -1; -} +} // namespace /** * Vote history. */ struct VoteRecord { private: // confidence's LSB bit is the result. Higher bits are actual confidence // score. uint16_t confidence = 0; // Historical record of votes. uint8_t votes = 0; // Each bit indicate if the vote is to be considered. uint8_t consider = 0; // How many in flight requests exists for this element. mutable std::atomic inflight{0}; // Seed for pseudorandom operations. const uint32_t seed = 0; // Track how many successful votes occured. uint32_t successfulVotes = 0; // Track the nodes which are part of the quorum. std::array nodeFilter{{0, 0, 0, 0, 0, 0, 0, 0}}; public: VoteRecord(bool accepted) : confidence(accepted) {} /** * Copy semantic */ VoteRecord(const VoteRecord &other) : confidence(other.confidence), votes(other.votes), consider(other.consider), inflight(other.inflight.load()), successfulVotes(other.successfulVotes), nodeFilter(other.nodeFilter) { } /** * Vote accounting facilities. */ bool isAccepted() const { return confidence & 0x01; } uint16_t getConfidence() const { return confidence >> 1; } bool hasFinalized() const { return getConfidence() >= AVALANCHE_FINALIZATION_SCORE; } /** * Register a new vote for an item and update confidence accordingly. * Returns true if the acceptance or finalization state changed. */ bool registerVote(NodeId nodeid, uint32_t error); /** * Register that a request is being made regarding that item. * The method is made const so that it can be accessed via a read only view * of vote_records. It's not a problem as it is made thread safe. */ bool registerPoll() const; /** * Return if this item is in condition to be polled at the moment. */ bool shouldPoll() const { return inflight < AVALANCHE_MAX_INFLIGHT_POLL; } /** * Clear `count` inflight requests. */ void clearInflightRequest(uint8_t count = 1) { inflight -= count; } private: /** * Add the node to the quorum. * Returns true if the node was added, false if the node already was in the * quorum. */ bool addNodeToQuorum(NodeId nodeid); }; class AvalancheVote { uint32_t error; uint256 hash; public: AvalancheVote() : error(-1), hash() {} AvalancheVote(uint32_t errorIn, uint256 hashIn) : error(errorIn), hash(hashIn) {} const uint256 &GetHash() const { return hash; } uint32_t GetError() const { return error; } // serialization support ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(error); READWRITE(hash); } }; class AvalancheResponse { uint64_t round; uint32_t cooldown; std::vector votes; public: AvalancheResponse(uint64_t roundIn, uint32_t cooldownIn, std::vector votesIn) : round(roundIn), cooldown(cooldownIn), votes(votesIn) {} uint64_t getRound() const { return round; } uint32_t getCooldown() const { return cooldown; } const std::vector &GetVotes() const { return votes; } // serialization support ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(round); READWRITE(cooldown); READWRITE(votes); } }; class AvalanchePoll { uint64_t round; std::vector invs; public: AvalanchePoll(uint64_t roundIn, std::vector invsIn) : round(roundIn), invs(invsIn) {} const std::vector &GetInvs() const { return invs; } // serialization support ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(round); READWRITE(invs); } }; class AvalancheBlockUpdate { union { CBlockIndex *pindex; uintptr_t raw; }; static const size_t STATUS_BITS = 2; static const uintptr_t MASK = (1 << STATUS_BITS) - 1; static_assert( alignof(CBlockIndex) >= (1 << STATUS_BITS), "CBlockIndex alignement doesn't allow for Status to be stored."); public: enum Status : uint8_t { Invalid, Rejected, Accepted, Finalized, }; AvalancheBlockUpdate(CBlockIndex *pindexIn, Status statusIn) : pindex(pindexIn) { raw |= statusIn; } Status getStatus() const { return Status(raw & MASK); } CBlockIndex *getBlockIndex() { return reinterpret_cast(raw & ~MASK); } const CBlockIndex *getBlockIndex() const { return const_cast(this)->getBlockIndex(); } }; typedef std::map BlockVoteMap; struct next_request_time {}; struct query_timeout {}; class AvalancheProcessor { private: CConnman *connman; std::chrono::milliseconds queryTimeoutDuration; /** * Blocks to run avalanche on. */ RWCollection vote_records; /** * Keep track of peers and queries sent. */ std::atomic round; typedef std::chrono::time_point TimePoint; struct Peer { NodeId nodeid; int64_t score; TimePoint nextRequestTime; }; typedef boost::multi_index_container< Peer, boost::multi_index::indexed_by< // index by nodeid boost::multi_index::hashed_unique< boost::multi_index::member>, // sorted by nextRequestTime boost::multi_index::ordered_non_unique< boost::multi_index::tag, boost::multi_index::member>>> PeerSet; RWCollection peerSet; struct Query { NodeId nodeid; uint64_t round; TimePoint timeout; /** * We declare this as mutable so it can be modified in the multi_index. * This is ok because we do not use this field to index in anyway. * * /!\ Do not use any mutable field as index. */ mutable std::vector invs; }; typedef boost::multi_index_container< Query, boost::multi_index::indexed_by< // index by nodeid/round boost::multi_index::ordered_unique< boost::multi_index::composite_key< Query, boost::multi_index::member, boost::multi_index::member>>, // sorted by timeout boost::multi_index::ordered_non_unique< boost::multi_index::tag, boost::multi_index::member>>> QuerySet; RWCollection queries; /** * Start stop machinery. */ std::atomic stopRequest; bool running GUARDED_BY(cs_running); Mutex cs_running; std::condition_variable cond_running; public: AvalancheProcessor(CConnman *connmanIn) : connman(connmanIn), queryTimeoutDuration( AVALANCHE_DEFAULT_QUERY_TIMEOUT_DURATION_MILLISECONDS), round(0), stopRequest(false), running(false) {} ~AvalancheProcessor() { stopEventLoop(); } void setQueryTimeoutDuration(std::chrono::milliseconds d) { queryTimeoutDuration = d; } bool addBlockToReconcile(const CBlockIndex *pindex); bool isAccepted(const CBlockIndex *pindex) const; int getConfidence(const CBlockIndex *pindex) const; bool registerVotes(NodeId nodeid, const AvalancheResponse &response, std::vector &updates); bool addPeer(NodeId nodeid, int64_t score); bool startEventLoop(CScheduler &scheduler); bool stopEventLoop(); private: void runEventLoop(); void clearTimedoutRequests(); std::vector getInvsForNextPoll(bool forPoll = true) const; NodeId getSuitableNodeToQuery(); friend struct AvalancheTest; }; #endif // BITCOIN_AVALANCHE_H diff --git a/src/bench/bench.h b/src/bench/bench.h index dc289d5ea..f53042978 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -1,144 +1,144 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_BENCH_BENCH_H #define BITCOIN_BENCH_BENCH_H #include #include #include #include #include #include #include #include // Simple micro-benchmarking framework; API mostly matches a subset of the // Google Benchmark framework (see https://github.com/google/benchmark) // Why not use the Google Benchmark framework? Because adding Yet Another // Dependency (that uses cmake as its build system and has lots of features we // don't need) isn't worth it. /* * Usage: static void CODE_TO_TIME(benchmark::State& state) { ... do any setup needed... while (state.KeepRunning()) { ... do stuff you want to time... } ... do any cleanup needed... } // default to running benchmark for 5000 iterations BENCHMARK(CODE_TO_TIME, 5000); */ namespace benchmark { // In case high_resolution_clock is steady, prefer that, otherwise use // steady_clock. struct best_clock { using hi_res_clock = std::chrono::high_resolution_clock; using steady_clock = std::chrono::steady_clock; using type = std::conditional::type; }; using clock = best_clock::type; using time_point = clock::time_point; using duration = clock::duration; class Printer; class State { public: std::string m_name; uint64_t m_num_iters_left; const uint64_t m_num_iters; const uint64_t m_num_evals; std::vector m_elapsed_results; time_point m_start_time; bool UpdateTimer(time_point finish_time); State(std::string name, uint64_t num_evals, double num_iters, Printer &printer) : m_name(name), m_num_iters_left(0), m_num_iters(num_iters), m_num_evals(num_evals) {} inline bool KeepRunning() { if (m_num_iters_left--) { return true; } bool result = UpdateTimer(clock::now()); // measure again so runtime of UpdateTimer is not included m_start_time = clock::now(); return result; } }; typedef std::function BenchFunction; class BenchRunner { struct Bench { BenchFunction func; uint64_t num_iters_for_one_second; }; typedef std::map BenchmarkMap; static BenchmarkMap &benchmarks(); public: BenchRunner(std::string name, BenchFunction func, uint64_t num_iters_for_one_second); static void RunAll(Printer &printer, uint64_t num_evals, double scaling, const std::string &filter, bool is_list_only); }; // interface to output benchmark results. class Printer { public: virtual ~Printer() {} virtual void header() = 0; virtual void result(const State &state) = 0; virtual void footer() = 0; }; // default printer to console, shows min, max, median. class ConsolePrinter : public Printer { public: void header(); void result(const State &state); void footer(); }; // creates box plot with plotly.js class PlotlyPrinter : public Printer { public: PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height); void header(); void result(const State &state); void footer(); private: std::string m_plotly_url; int64_t m_width; int64_t m_height; }; -} +} // namespace benchmark // BENCHMARK(foo, num_iters_for_one_second) expands to: benchmark::BenchRunner // bench_11foo("foo", num_iterations); // Choose a num_iters_for_one_second that takes roughly 1 second. The goal is // that all benchmarks should take approximately // the same time, and scaling factor can be used that the total time is // appropriate for your system. #define BENCHMARK(n, num_iters_for_one_second) \ benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))( \ BOOST_PP_STRINGIZE(n), n, (num_iters_for_one_second)); #endif // BITCOIN_BENCH_BENCH_H diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index d8c28a376..d49f2b99e 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -1,57 +1,57 @@ // Copyright (c) 2016 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 #include #include #include #include namespace block_bench { #include -} +} // namespace block_bench // These are the two major time-sinks which happen after we have fully received // a block off the wire, but before we can relay the block on to peers using // compact block relay. static void DeserializeBlockTest(benchmark::State &state) { CDataStream stream((const char *)block_bench::block413567, (const char *)&block_bench::block413567[sizeof( block_bench::block413567)], SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; stream.write(&a, 1); // Prevent compaction while (state.KeepRunning()) { CBlock block; stream >> block; assert(stream.Rewind(sizeof(block_bench::block413567))); } } static void DeserializeAndCheckBlockTest(benchmark::State &state) { CDataStream stream((const char *)block_bench::block413567, (const char *)&block_bench::block413567[sizeof( block_bench::block413567)], SER_NETWORK, PROTOCOL_VERSION); char a = '\0'; stream.write(&a, 1); // Prevent compaction const Config &config = GetConfig(); while (state.KeepRunning()) { // Note that CBlock caches its checked state, so we need to recreate it // here. CBlock block; stream >> block; assert(stream.Rewind(sizeof(block_bench::block413567))); CValidationState validationState; assert(CheckBlock(config, block, validationState)); } } BENCHMARK(DeserializeBlockTest, 130); BENCHMARK(DeserializeAndCheckBlockTest, 160); diff --git a/src/crypto/sha256_avx2.cpp b/src/crypto/sha256_avx2.cpp index 1aa70c8cc..bb19b9709 100644 --- a/src/crypto/sha256_avx2.cpp +++ b/src/crypto/sha256_avx2.cpp @@ -1,454 +1,454 @@ #ifdef ENABLE_AVX2 #include #include #include #include namespace sha256d64_avx2 { namespace { __m256i inline K(uint32_t x) { return _mm256_set1_epi32(x); } __m256i inline Add(__m256i x, __m256i y) { return _mm256_add_epi32(x, y); } __m256i inline Add(__m256i x, __m256i y, __m256i z) { return Add(Add(x, y), z); } __m256i inline Add(__m256i x, __m256i y, __m256i z, __m256i w) { return Add(Add(x, y), Add(z, w)); } __m256i inline Add(__m256i x, __m256i y, __m256i z, __m256i w, __m256i v) { return Add(Add(x, y, z), Add(w, v)); } __m256i inline Inc(__m256i &x, __m256i y) { x = Add(x, y); return x; } __m256i inline Inc(__m256i &x, __m256i y, __m256i z) { x = Add(x, y, z); return x; } __m256i inline Inc(__m256i &x, __m256i y, __m256i z, __m256i w) { x = Add(x, y, z, w); return x; } __m256i inline Xor(__m256i x, __m256i y) { return _mm256_xor_si256(x, y); } __m256i inline Xor(__m256i x, __m256i y, __m256i z) { return Xor(Xor(x, y), z); } __m256i inline Or(__m256i x, __m256i y) { return _mm256_or_si256(x, y); } __m256i inline And(__m256i x, __m256i y) { return _mm256_and_si256(x, y); } __m256i inline ShR(__m256i x, int n) { return _mm256_srli_epi32(x, n); } __m256i inline ShL(__m256i x, int n) { return _mm256_slli_epi32(x, n); } __m256i inline Ch(__m256i x, __m256i y, __m256i z) { return Xor(z, And(x, Xor(y, z))); } __m256i inline Maj(__m256i x, __m256i y, __m256i z) { return Or(And(x, y), And(z, Or(x, y))); } __m256i inline Sigma0(__m256i x) { return Xor(Or(ShR(x, 2), ShL(x, 30)), Or(ShR(x, 13), ShL(x, 19)), Or(ShR(x, 22), ShL(x, 10))); } __m256i inline Sigma1(__m256i x) { return Xor(Or(ShR(x, 6), ShL(x, 26)), Or(ShR(x, 11), ShL(x, 21)), Or(ShR(x, 25), ShL(x, 7))); } __m256i inline sigma0(__m256i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR(x, 18), ShL(x, 14)), ShR(x, 3)); } __m256i inline sigma1(__m256i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ inline void __attribute__((always_inline)) Round(__m256i a, __m256i b, __m256i c, __m256i &d, __m256i e, __m256i f, __m256i g, __m256i &h, __m256i k) { __m256i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m256i t2 = Add(Sigma0(a), Maj(a, b, c)); d = Add(d, t1); h = Add(t1, t2); } __m256i inline Read8(const uint8_t *chunk, int offset) { __m256i ret = _mm256_set_epi32( ReadLE32(chunk + 0 + offset), ReadLE32(chunk + 64 + offset), ReadLE32(chunk + 128 + offset), ReadLE32(chunk + 192 + offset), ReadLE32(chunk + 256 + offset), ReadLE32(chunk + 320 + offset), ReadLE32(chunk + 384 + offset), ReadLE32(chunk + 448 + offset)); return _mm256_shuffle_epi8( ret, _mm256_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL, 0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); } inline void Write8(uint8_t *out, int offset, __m256i v) { v = _mm256_shuffle_epi8( v, _mm256_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL, 0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); WriteLE32(out + 0 + offset, _mm256_extract_epi32(v, 7)); WriteLE32(out + 32 + offset, _mm256_extract_epi32(v, 6)); WriteLE32(out + 64 + offset, _mm256_extract_epi32(v, 5)); WriteLE32(out + 96 + offset, _mm256_extract_epi32(v, 4)); WriteLE32(out + 128 + offset, _mm256_extract_epi32(v, 3)); WriteLE32(out + 160 + offset, _mm256_extract_epi32(v, 2)); WriteLE32(out + 192 + offset, _mm256_extract_epi32(v, 1)); WriteLE32(out + 224 + offset, _mm256_extract_epi32(v, 0)); } -} +} // namespace void Transform_8way(uint8_t *out, const uint8_t *in) { // Transform 1 __m256i a = K(0x6a09e667ul); __m256i b = K(0xbb67ae85ul); __m256i c = K(0x3c6ef372ul); __m256i d = K(0xa54ff53aul); __m256i e = K(0x510e527ful); __m256i f = K(0x9b05688cul); __m256i g = K(0x1f83d9abul); __m256i h = K(0x5be0cd19ul); __m256i w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0 = Read8(in, 0))); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1 = Read8(in, 4))); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2 = Read8(in, 8))); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3 = Read8(in, 12))); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4 = Read8(in, 16))); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5 = Read8(in, 20))); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6 = Read8(in, 24))); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7 = Read8(in, 28))); Round(a, b, c, d, e, f, g, h, Add(K(0xd807aa98ul), w8 = Read8(in, 32))); Round(h, a, b, c, d, e, f, g, Add(K(0x12835b01ul), w9 = Read8(in, 36))); Round(g, h, a, b, c, d, e, f, Add(K(0x243185beul), w10 = Read8(in, 40))); Round(f, g, h, a, b, c, d, e, Add(K(0x550c7dc3ul), w11 = Read8(in, 44))); Round(e, f, g, h, a, b, c, d, Add(K(0x72be5d74ul), w12 = Read8(in, 48))); Round(d, e, f, g, h, a, b, c, Add(K(0x80deb1feul), w13 = Read8(in, 52))); Round(c, d, e, f, g, h, a, b, Add(K(0x9bdc06a7ul), w14 = Read8(in, 56))); Round(b, c, d, e, f, g, h, a, Add(K(0xc19bf174ul), w15 = Read8(in, 60))); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); a = Add(a, K(0x6a09e667ul)); b = Add(b, K(0xbb67ae85ul)); c = Add(c, K(0x3c6ef372ul)); d = Add(d, K(0xa54ff53aul)); e = Add(e, K(0x510e527ful)); f = Add(f, K(0x9b05688cul)); g = Add(g, K(0x1f83d9abul)); h = Add(h, K(0x5be0cd19ul)); __m256i t0 = a, t1 = b, t2 = c, t3 = d, t4 = e, t5 = f, t6 = g, t7 = h; // Transform 2 Round(a, b, c, d, e, f, g, h, K(0xc28a2f98ul)); Round(h, a, b, c, d, e, f, g, K(0x71374491ul)); Round(g, h, a, b, c, d, e, f, K(0xb5c0fbcful)); Round(f, g, h, a, b, c, d, e, K(0xe9b5dba5ul)); Round(e, f, g, h, a, b, c, d, K(0x3956c25bul)); Round(d, e, f, g, h, a, b, c, K(0x59f111f1ul)); Round(c, d, e, f, g, h, a, b, K(0x923f82a4ul)); Round(b, c, d, e, f, g, h, a, K(0xab1c5ed5ul)); Round(a, b, c, d, e, f, g, h, K(0xd807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf374ul)); Round(a, b, c, d, e, f, g, h, K(0x649b69c1ul)); Round(h, a, b, c, d, e, f, g, K(0xf0fe4786ul)); Round(g, h, a, b, c, d, e, f, K(0x0fe1edc6ul)); Round(f, g, h, a, b, c, d, e, K(0x240cf254ul)); Round(e, f, g, h, a, b, c, d, K(0x4fe9346ful)); Round(d, e, f, g, h, a, b, c, K(0x6cc984beul)); Round(c, d, e, f, g, h, a, b, K(0x61b9411eul)); Round(b, c, d, e, f, g, h, a, K(0x16f988faul)); Round(a, b, c, d, e, f, g, h, K(0xf2c65152ul)); Round(h, a, b, c, d, e, f, g, K(0xa88e5a6dul)); Round(g, h, a, b, c, d, e, f, K(0xb019fc65ul)); Round(f, g, h, a, b, c, d, e, K(0xb9d99ec7ul)); Round(e, f, g, h, a, b, c, d, K(0x9a1231c3ul)); Round(d, e, f, g, h, a, b, c, K(0xe70eeaa0ul)); Round(c, d, e, f, g, h, a, b, K(0xfdb1232bul)); Round(b, c, d, e, f, g, h, a, K(0xc7353eb0ul)); Round(a, b, c, d, e, f, g, h, K(0x3069bad5ul)); Round(h, a, b, c, d, e, f, g, K(0xcb976d5ful)); Round(g, h, a, b, c, d, e, f, K(0x5a0f118ful)); Round(f, g, h, a, b, c, d, e, K(0xdc1eeefdul)); Round(e, f, g, h, a, b, c, d, K(0x0a35b689ul)); Round(d, e, f, g, h, a, b, c, K(0xde0b7a04ul)); Round(c, d, e, f, g, h, a, b, K(0x58f4ca9dul)); Round(b, c, d, e, f, g, h, a, K(0xe15d5b16ul)); Round(a, b, c, d, e, f, g, h, K(0x007f3e86ul)); Round(h, a, b, c, d, e, f, g, K(0x37088980ul)); Round(g, h, a, b, c, d, e, f, K(0xa507ea32ul)); Round(f, g, h, a, b, c, d, e, K(0x6fab9537ul)); Round(e, f, g, h, a, b, c, d, K(0x17406110ul)); Round(d, e, f, g, h, a, b, c, K(0x0d8cd6f1ul)); Round(c, d, e, f, g, h, a, b, K(0xcdaa3b6dul)); Round(b, c, d, e, f, g, h, a, K(0xc0bbbe37ul)); Round(a, b, c, d, e, f, g, h, K(0x83613bdaul)); Round(h, a, b, c, d, e, f, g, K(0xdb48a363ul)); Round(g, h, a, b, c, d, e, f, K(0x0b02e931ul)); Round(f, g, h, a, b, c, d, e, K(0x6fd15ca7ul)); Round(e, f, g, h, a, b, c, d, K(0x521afacaul)); Round(d, e, f, g, h, a, b, c, K(0x31338431ul)); Round(c, d, e, f, g, h, a, b, K(0x6ed41a95ul)); Round(b, c, d, e, f, g, h, a, K(0x6d437890ul)); Round(a, b, c, d, e, f, g, h, K(0xc39c91f2ul)); Round(h, a, b, c, d, e, f, g, K(0x9eccabbdul)); Round(g, h, a, b, c, d, e, f, K(0xb5c9a0e6ul)); Round(f, g, h, a, b, c, d, e, K(0x532fb63cul)); Round(e, f, g, h, a, b, c, d, K(0xd2c741c6ul)); Round(d, e, f, g, h, a, b, c, K(0x07237ea3ul)); Round(c, d, e, f, g, h, a, b, K(0xa4954b68ul)); Round(b, c, d, e, f, g, h, a, K(0x4c191d76ul)); w0 = Add(t0, a); w1 = Add(t1, b); w2 = Add(t2, c); w3 = Add(t3, d); w4 = Add(t4, e); w5 = Add(t5, f); w6 = Add(t6, g); w7 = Add(t7, h); // Transform 3 a = K(0x6a09e667ul); b = K(0xbb67ae85ul); c = K(0x3c6ef372ul); d = K(0xa54ff53aul); e = K(0x510e527ful); f = K(0x9b05688cul); g = K(0x1f83d9abul); h = K(0x5be0cd19ul); Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0)); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1)); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2)); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3)); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4)); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5)); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6)); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7)); Round(a, b, c, d, e, f, g, h, K(0x5807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf274ul)); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, K(0xa00000ul), sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), K(0x100ul), sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, K(0x11002000ul)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), w8 = Add(K(0x80000000ul), sigma1(w6), w1))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), w9 = Add(sigma1(w7), w2))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), w10 = Add(sigma1(w8), w3))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), w11 = Add(sigma1(w9), w4))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), w12 = Add(sigma1(w10), w5))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), w13 = Add(sigma1(w11), w6))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), w14 = Add(sigma1(w12), w7, K(0x400022ul)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), w15 = Add(K(0x100ul), sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), w14, sigma1(w12), w7, sigma0(w15))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), w15, sigma1(w13), w8, sigma0(w0))); // Output Write8(out, 0, Add(a, K(0x6a09e667ul))); Write8(out, 4, Add(b, K(0xbb67ae85ul))); Write8(out, 8, Add(c, K(0x3c6ef372ul))); Write8(out, 12, Add(d, K(0xa54ff53aul))); Write8(out, 16, Add(e, K(0x510e527ful))); Write8(out, 20, Add(f, K(0x9b05688cul))); Write8(out, 24, Add(g, K(0x1f83d9abul))); Write8(out, 28, Add(h, K(0x5be0cd19ul))); } -} +} // namespace sha256d64_avx2 #endif diff --git a/src/crypto/sha256_shani.cpp b/src/crypto/sha256_shani.cpp index 94de3a728..820f262a9 100644 --- a/src/crypto/sha256_shani.cpp +++ b/src/crypto/sha256_shani.cpp @@ -1,358 +1,358 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // Based on https://github.com/noloader/SHA-Intrinsics/blob/master/sha256-x86.c, // Written and placed in public domain by Jeffrey Walton. // Based on code from Intel, and by Sean Gulley for the miTLS project. #ifdef ENABLE_SHANI #include #include #include namespace { const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); const __m128i INIT0 = _mm_set_epi64x(0x6a09e667bb67ae85ull, 0x510e527f9b05688cull); const __m128i INIT1 = _mm_set_epi64x(0x3c6ef372a54ff53aull, 0x1f83d9ab5be0cd19ull); inline void __attribute__((always_inline)) QuadRound(__m128i &state0, __m128i &state1, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_set_epi64x(k1, k0); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } inline void __attribute__((always_inline)) QuadRound(__m128i &state0, __m128i &state1, __m128i m, uint64_t k1, uint64_t k0) { const __m128i msg = _mm_add_epi32(m, _mm_set_epi64x(k1, k0)); state1 = _mm_sha256rnds2_epu32(state1, state0, msg); state0 = _mm_sha256rnds2_epu32(state0, state1, _mm_shuffle_epi32(msg, 0x0e)); } inline void __attribute__((always_inline)) ShiftMessageA(__m128i &m0, __m128i m1) { m0 = _mm_sha256msg1_epu32(m0, m1); } inline void __attribute__((always_inline)) ShiftMessageC(__m128i &m0, __m128i m1, __m128i &m2) { m2 = _mm_sha256msg2_epu32(_mm_add_epi32(m2, _mm_alignr_epi8(m1, m0, 4)), m1); } inline void __attribute__((always_inline)) ShiftMessageB(__m128i &m0, __m128i m1, __m128i &m2) { ShiftMessageC(m0, m1, m2); ShiftMessageA(m0, m1); } inline void __attribute__((always_inline)) Shuffle(__m128i &s0, __m128i &s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0xB1); const __m128i t2 = _mm_shuffle_epi32(s1, 0x1B); s0 = _mm_alignr_epi8(t1, t2, 0x08); s1 = _mm_blend_epi16(t2, t1, 0xF0); } inline void __attribute__((always_inline)) Unshuffle(__m128i &s0, __m128i &s1) { const __m128i t1 = _mm_shuffle_epi32(s0, 0x1B); const __m128i t2 = _mm_shuffle_epi32(s1, 0xB1); s0 = _mm_blend_epi16(t1, t2, 0xF0); s1 = _mm_alignr_epi8(t2, t1, 0x08); } __m128i inline __attribute__((always_inline)) Load(const uint8_t *in) { return _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)in), MASK); } inline void __attribute__((always_inline)) Save(uint8_t *out, __m128i s) { _mm_storeu_si128((__m128i *)out, _mm_shuffle_epi8(s, MASK)); } -} +} // namespace namespace sha256_shani { void Transform(uint32_t *s, const uint8_t *chunk, size_t blocks) { __m128i m0, m1, m2, m3, s0, s1, so0, so1; /* Load state */ s0 = _mm_loadu_si128((const __m128i *)s); s1 = _mm_loadu_si128((const __m128i *)(s + 4)); Shuffle(s0, s1); while (blocks--) { /* Remember old state */ so0 = s0; so1 = s1; /* Load data and transform */ m0 = Load(chunk); QuadRound(s0, s1, m0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); m1 = Load(chunk + 16); QuadRound(s0, s1, m1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(m0, m1); m2 = Load(chunk + 32); QuadRound(s0, s1, m2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); ShiftMessageA(m1, m2); m3 = Load(chunk + 48); QuadRound(s0, s1, m3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(m0, m1, m2); QuadRound(s0, s1, m2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(m1, m2, m3); QuadRound(s0, s1, m3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(m0, m1, m2); QuadRound(s0, s1, m2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); ShiftMessageB(m1, m2, m3); QuadRound(s0, s1, m3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(m2, m3, m0); QuadRound(s0, s1, m0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(m3, m0, m1); QuadRound(s0, s1, m1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(m0, m1, m2); QuadRound(s0, s1, m2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(m1, m2, m3); QuadRound(s0, s1, m3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); /* Combine with old state */ s0 = _mm_add_epi32(s0, so0); s1 = _mm_add_epi32(s1, so1); /* Advance */ chunk += 64; } Unshuffle(s0, s1); _mm_storeu_si128((__m128i *)s, s0); _mm_storeu_si128((__m128i *)(s + 4), s1); } -} +} // namespace sha256_shani namespace sha256d64_shani { void Transform_2way(uint8_t *out, const uint8_t *in) { __m128i am0, am1, am2, am3, as0, as1, aso0, aso1; __m128i bm0, bm1, bm2, bm3, bs0, bs1, bso0, bso1; /* Transform 1 */ bs0 = as0 = INIT0; bs1 = as1 = INIT1; am0 = Load(in); bm0 = Load(in + 64); QuadRound(as0, as1, am0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); QuadRound(bs0, bs1, bm0, 0xe9b5dba5b5c0fbcfull, 0x71374491428a2f98ull); am1 = Load(in + 16); bm1 = Load(in + 80); QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(am0, am1); ShiftMessageA(bm0, bm1); am2 = Load(in + 32); bm2 = Load(in + 96); QuadRound(as0, as1, am2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(bs0, bs1, bm2, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); ShiftMessageA(am1, am2); ShiftMessageA(bm1, bm2); am3 = Load(in + 48); bm3 = Load(in + 112); QuadRound(as0, as1, am3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, bm3, 0xc19bf1749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786E49b69c1ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); QuadRound(bs0, bs1, bm2, 0xc76c51A3c24b8b70ull, 0xa81a664ba2bfe8a1ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(am0, am1, am2); ShiftMessageC(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(am1, am2, am3); ShiftMessageC(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); QuadRound(bs0, bs1, bm3, 0xc67178f2bef9A3f7ull, 0xa4506ceb90befffaull); as0 = _mm_add_epi32(as0, INIT0); bs0 = _mm_add_epi32(bs0, INIT0); as1 = _mm_add_epi32(as1, INIT1); bs1 = _mm_add_epi32(bs1, INIT1); /* Transform 2 */ aso0 = as0; bso0 = bs0; aso1 = as1; bso1 = bs1; QuadRound(as0, as1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); QuadRound(bs0, bs1, 0xe9b5dba5b5c0fbcfull, 0x71374491c28a2f98ull); QuadRound(as0, as1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b01d807aa98ull); QuadRound(as0, as1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, 0xc19bf3749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(as0, as1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); QuadRound(bs0, bs1, 0x240cf2540fe1edc6ull, 0xf0fe4786649b69c1ull); QuadRound(as0, as1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); QuadRound(bs0, bs1, 0x16f988fa61b9411eull, 0x6cc984be4fe9346full); QuadRound(as0, as1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); QuadRound(bs0, bs1, 0xb9d99ec7b019fc65ull, 0xa88e5a6df2c65152ull); QuadRound(as0, as1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); QuadRound(bs0, bs1, 0xc7353eb0fdb1232bull, 0xe70eeaa09a1231c3ull); QuadRound(as0, as1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); QuadRound(bs0, bs1, 0xdc1eeefd5a0f118full, 0xcb976d5f3069bad5ull); QuadRound(as0, as1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); QuadRound(bs0, bs1, 0xe15d5b1658f4ca9dull, 0xde0b7a040a35b689ull); QuadRound(as0, as1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); QuadRound(bs0, bs1, 0x6fab9537a507ea32ull, 0x37088980007f3e86ull); QuadRound(as0, as1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); QuadRound(bs0, bs1, 0xc0bbbe37cdaa3b6dull, 0x0d8cd6f117406110ull); QuadRound(as0, as1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); QuadRound(bs0, bs1, 0x6fd15ca70b02e931ull, 0xdb48a36383613bdaull); QuadRound(as0, as1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); QuadRound(bs0, bs1, 0x6d4378906ed41a95ull, 0x31338431521afacaull); QuadRound(as0, as1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); QuadRound(bs0, bs1, 0x532fb63cb5c9a0e6ull, 0x9eccabbdc39c91f2ull); QuadRound(as0, as1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); QuadRound(bs0, bs1, 0x4c191d76a4954b68ull, 0x07237ea3d2c741c6ull); as0 = _mm_add_epi32(as0, aso0); bs0 = _mm_add_epi32(bs0, bso0); as1 = _mm_add_epi32(as1, aso1); bs1 = _mm_add_epi32(bs1, bso1); /* Extract hash */ Unshuffle(as0, as1); Unshuffle(bs0, bs1); am0 = as0; bm0 = bs0; am1 = as1; bm1 = bs1; /* Transform 3 */ bs0 = as0 = INIT0; bs1 = as1 = INIT1; QuadRound(as0, as1, am0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); QuadRound(bs0, bs1, bm0, 0xe9b5dba5B5c0fbcfull, 0x71374491428a2f98ull); QuadRound(as0, as1, am1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); QuadRound(bs0, bs1, bm1, 0xab1c5ed5923f82a4ull, 0x59f111f13956c25bull); ShiftMessageA(am0, am1); ShiftMessageA(bm0, bm1); bm2 = am2 = _mm_set_epi64x(0x0ull, 0x80000000ull); QuadRound(as0, as1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); QuadRound(bs0, bs1, 0x550c7dc3243185beull, 0x12835b015807aa98ull); ShiftMessageA(am1, am2); ShiftMessageA(bm1, bm2); bm3 = am3 = _mm_set_epi64x(0x10000000000ull, 0x0ull); QuadRound(as0, as1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); QuadRound(bs0, bs1, 0xc19bf2749bdc06a7ull, 0x80deb1fe72be5d74ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); QuadRound(bs0, bs1, bm0, 0x240ca1cc0fc19dc6ull, 0xefbe4786e49b69c1ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); QuadRound(bs0, bs1, bm1, 0x76f988da5cb0a9dcull, 0x4a7484aa2de92c6full); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); QuadRound(bs0, bs1, bm2, 0xbf597fc7b00327c8ull, 0xa831c66d983e5152ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); QuadRound(bs0, bs1, bm3, 0x1429296706ca6351ull, 0xd5a79147c6e00bf3ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); QuadRound(bs0, bs1, bm0, 0x53380d134d2c6dfcull, 0x2e1b213827b70a85ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); QuadRound(bs0, bs1, bm1, 0x92722c8581c2c92eull, 0x766a0abb650a7354ull); ShiftMessageB(am0, am1, am2); ShiftMessageB(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); QuadRound(bs0, bs1, bm2, 0xc76c51a3c24b8b70ull, 0xa81a664ba2bfe8A1ull); ShiftMessageB(am1, am2, am3); ShiftMessageB(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); QuadRound(bs0, bs1, bm3, 0x106aa070f40e3585ull, 0xd6990624d192e819ull); ShiftMessageB(am2, am3, am0); ShiftMessageB(bm2, bm3, bm0); QuadRound(as0, as1, am0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); QuadRound(bs0, bs1, bm0, 0x34b0bcb52748774cull, 0x1e376c0819a4c116ull); ShiftMessageB(am3, am0, am1); ShiftMessageB(bm3, bm0, bm1); QuadRound(as0, as1, am1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); QuadRound(bs0, bs1, bm1, 0x682e6ff35b9cca4full, 0x4ed8aa4a391c0cb3ull); ShiftMessageC(am0, am1, am2); ShiftMessageC(bm0, bm1, bm2); QuadRound(as0, as1, am2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); QuadRound(bs0, bs1, bm2, 0x8cc7020884c87814ull, 0x78a5636f748f82eeull); ShiftMessageC(am1, am2, am3); ShiftMessageC(bm1, bm2, bm3); QuadRound(as0, as1, am3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); QuadRound(bs0, bs1, bm3, 0xc67178f2bef9a3f7ull, 0xa4506ceb90befffaull); as0 = _mm_add_epi32(as0, INIT0); bs0 = _mm_add_epi32(bs0, INIT0); as1 = _mm_add_epi32(as1, INIT1); bs1 = _mm_add_epi32(bs1, INIT1); /* Extract hash into out */ Unshuffle(as0, as1); Unshuffle(bs0, bs1); Save(out, as0); Save(out + 16, as1); Save(out + 32, bs0); Save(out + 48, bs1); } -} +} // namespace sha256d64_shani #endif diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp index 1e869836f..9a861dbf5 100644 --- a/src/crypto/sha256_sse4.cpp +++ b/src/crypto/sha256_sse4.cpp @@ -1,1506 +1,1506 @@ // Copyright (c) 2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // // This is a translation to GCC extended asm syntax from YASM code by Intel // (available at the bottom of this file). #include #include #if defined(__x86_64__) || defined(__amd64__) namespace sha256_sse4 { void Transform(uint32_t *s, const uint8_t *chunk, size_t blocks) { static const uint32_t K256 alignas(16)[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; static const uint32_t FLIP_MASK alignas(16)[] = {0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f}; static const uint32_t SHUF_00BA alignas(16)[] = {0x03020100, 0x0b0a0908, 0xffffffff, 0xffffffff}; static const uint32_t SHUF_DC00 alignas(16)[] = {0xffffffff, 0xffffffff, 0x03020100, 0x0b0a0908}; uint32_t a, b, c, d, f, g, h, y0, y1, y2; uint64_t tbl; uint64_t inp_end, inp; uint32_t xfer alignas(16)[4]; __asm__ __volatile__( "shl $0x6,%2;" "je Ldone_hash_%=;" "add %1,%2;" "mov %2,%14;" "mov (%0),%3;" "mov 0x4(%0),%4;" "mov 0x8(%0),%5;" "mov 0xc(%0),%6;" "mov 0x10(%0),%k2;" "mov 0x14(%0),%7;" "mov 0x18(%0),%8;" "mov 0x1c(%0),%9;" "movdqa %18,%%xmm12;" "movdqa %19,%%xmm10;" "movdqa %20,%%xmm11;" "Lloop0_%=:" "lea %17,%13;" "movdqu (%1),%%xmm4;" "pshufb %%xmm12,%%xmm4;" "movdqu 0x10(%1),%%xmm5;" "pshufb %%xmm12,%%xmm5;" "movdqu 0x20(%1),%%xmm6;" "pshufb %%xmm12,%%xmm6;" "movdqu 0x30(%1),%%xmm7;" "pshufb %%xmm12,%%xmm7;" "mov %1,%15;" "mov $3,%1;" "Lloop1_%=:" "movdqa 0x0(%13),%%xmm9;" "paddd %%xmm4,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm7,%%xmm0;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "palignr $0x4,%%xmm6,%%xmm0;" "ror $0x9,%11;" "xor %k2,%10;" "mov %7,%12;" "ror $0x5,%10;" "movdqa %%xmm5,%%xmm1;" "xor %3,%11;" "xor %8,%12;" "paddd %%xmm4,%%xmm0;" "xor %k2,%10;" "and %k2,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm4,%%xmm1;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "pslld $0x19,%%xmm1;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "psrld $0x7,%%xmm2;" "and %4,%10;" "add %11,%9;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%9;" "movdqa %%xmm3,%%xmm2;" "mov %6,%10;" "mov %9,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %6,%10;" "mov %k2,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %6,%10;" "and %6,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %9,%11;" "xor %7,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "pxor %%xmm8,%%xmm1;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "pshufd $0xfa,%%xmm7,%%xmm2;" "and %3,%10;" "add %11,%8;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%8;" "movdqa %%xmm2,%%xmm3;" "mov %5,%10;" "mov %8,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %k2,%12;" "psrlq $0x13,%%xmm3;" "xor %5,%10;" "and %5,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %8,%11;" "xor %k2,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "pshufb %%xmm10,%%xmm8;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "paddd %%xmm8,%%xmm0;" "and %9,%10;" "add %11,%7;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%7;" "movdqa %%xmm2,%%xmm3;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "movdqa %%xmm2,%%xmm4;" "ror $0x9,%11;" "xor %4,%10;" "mov %5,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %7,%11;" "xor %6,%12;" "psrlq $0x13,%%xmm3;" "xor %4,%10;" "and %4,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm4;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm4;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "pshufb %%xmm11,%%xmm4;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "paddd %%xmm0,%%xmm4;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "movdqa 0x10(%13),%%xmm9;" "paddd %%xmm5,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm4,%%xmm0;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "palignr $0x4,%%xmm7,%%xmm0;" "ror $0x9,%11;" "xor %3,%10;" "mov %4,%12;" "ror $0x5,%10;" "movdqa %%xmm6,%%xmm1;" "xor %k2,%11;" "xor %5,%12;" "paddd %%xmm5,%%xmm0;" "xor %3,%10;" "and %3,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm5,%%xmm1;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "pslld $0x19,%%xmm1;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "psrld $0x7,%%xmm2;" "and %7,%10;" "add %11,%6;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%6;" "movdqa %%xmm3,%%xmm2;" "mov %9,%10;" "mov %6,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %9,%10;" "mov %3,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %9,%10;" "and %9,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %6,%11;" "xor %4,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "pxor %%xmm8,%%xmm1;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "pshufd $0xfa,%%xmm4,%%xmm2;" "and %k2,%10;" "add %11,%5;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%5;" "movdqa %%xmm2,%%xmm3;" "mov %8,%10;" "mov %5,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %3,%12;" "psrlq $0x13,%%xmm3;" "xor %8,%10;" "and %8,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %5,%11;" "xor %3,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "pshufb %%xmm10,%%xmm8;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "paddd %%xmm8,%%xmm0;" "and %6,%10;" "add %11,%4;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%4;" "movdqa %%xmm2,%%xmm3;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "movdqa %%xmm2,%%xmm5;" "ror $0x9,%11;" "xor %7,%10;" "mov %8,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %4,%11;" "xor %9,%12;" "psrlq $0x13,%%xmm3;" "xor %7,%10;" "and %7,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm5;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm5;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "pshufb %%xmm11,%%xmm5;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "paddd %%xmm0,%%xmm5;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "movdqa 0x20(%13),%%xmm9;" "paddd %%xmm6,%%xmm9;" "movdqa %%xmm9,%16;" "movdqa %%xmm5,%%xmm0;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "palignr $0x4,%%xmm4,%%xmm0;" "ror $0x9,%11;" "xor %k2,%10;" "mov %7,%12;" "ror $0x5,%10;" "movdqa %%xmm7,%%xmm1;" "xor %3,%11;" "xor %8,%12;" "paddd %%xmm6,%%xmm0;" "xor %k2,%10;" "and %k2,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm6,%%xmm1;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "pslld $0x19,%%xmm1;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "psrld $0x7,%%xmm2;" "and %4,%10;" "add %11,%9;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%9;" "movdqa %%xmm3,%%xmm2;" "mov %6,%10;" "mov %9,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %6,%10;" "mov %k2,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %6,%10;" "and %6,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %9,%11;" "xor %7,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "pxor %%xmm8,%%xmm1;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "pshufd $0xfa,%%xmm5,%%xmm2;" "and %3,%10;" "add %11,%8;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%8;" "movdqa %%xmm2,%%xmm3;" "mov %5,%10;" "mov %8,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %k2,%12;" "psrlq $0x13,%%xmm3;" "xor %5,%10;" "and %5,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %8,%11;" "xor %k2,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "pshufb %%xmm10,%%xmm8;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "paddd %%xmm8,%%xmm0;" "and %9,%10;" "add %11,%7;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%7;" "movdqa %%xmm2,%%xmm3;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "movdqa %%xmm2,%%xmm6;" "ror $0x9,%11;" "xor %4,%10;" "mov %5,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %7,%11;" "xor %6,%12;" "psrlq $0x13,%%xmm3;" "xor %4,%10;" "and %4,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm6;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm6;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "pshufb %%xmm11,%%xmm6;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "paddd %%xmm0,%%xmm6;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "movdqa 0x30(%13),%%xmm9;" "paddd %%xmm7,%%xmm9;" "movdqa %%xmm9,%16;" "add $0x40,%13;" "movdqa %%xmm6,%%xmm0;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "palignr $0x4,%%xmm5,%%xmm0;" "ror $0x9,%11;" "xor %3,%10;" "mov %4,%12;" "ror $0x5,%10;" "movdqa %%xmm4,%%xmm1;" "xor %k2,%11;" "xor %5,%12;" "paddd %%xmm7,%%xmm0;" "xor %3,%10;" "and %3,%12;" "ror $0xb,%11;" "palignr $0x4,%%xmm7,%%xmm1;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "movdqa %%xmm1,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add %16,%12;" "movdqa %%xmm1,%%xmm3;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "pslld $0x19,%%xmm1;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "psrld $0x7,%%xmm2;" "and %7,%10;" "add %11,%6;" "por %%xmm2,%%xmm1;" "or %12,%10;" "add %10,%6;" "movdqa %%xmm3,%%xmm2;" "mov %9,%10;" "mov %6,%11;" "movdqa %%xmm3,%%xmm8;" "ror $0xe,%10;" "xor %9,%10;" "mov %3,%12;" "ror $0x9,%11;" "pslld $0xe,%%xmm3;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "psrld $0x12,%%xmm2;" "ror $0xb,%11;" "xor %9,%10;" "and %9,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm1;" "xor %6,%11;" "xor %4,%12;" "psrld $0x3,%%xmm8;" "add %10,%12;" "add 4+%16,%12;" "ror $0x2,%11;" "pxor %%xmm2,%%xmm1;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "pxor %%xmm8,%%xmm1;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "pshufd $0xfa,%%xmm6,%%xmm2;" "and %k2,%10;" "add %11,%5;" "paddd %%xmm1,%%xmm0;" "or %12,%10;" "add %10,%5;" "movdqa %%xmm2,%%xmm3;" "mov %8,%10;" "mov %5,%11;" "ror $0xe,%10;" "movdqa %%xmm2,%%xmm8;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %3,%12;" "psrlq $0x13,%%xmm3;" "xor %8,%10;" "and %8,%12;" "psrld $0xa,%%xmm8;" "ror $0xb,%11;" "xor %5,%11;" "xor %3,%12;" "ror $0x6,%10;" "pxor %%xmm3,%%xmm2;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "pxor %%xmm2,%%xmm8;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "pshufb %%xmm10,%%xmm8;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "paddd %%xmm8,%%xmm0;" "and %6,%10;" "add %11,%4;" "pshufd $0x50,%%xmm0,%%xmm2;" "or %12,%10;" "add %10,%4;" "movdqa %%xmm2,%%xmm3;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "movdqa %%xmm2,%%xmm7;" "ror $0x9,%11;" "xor %7,%10;" "mov %8,%12;" "ror $0x5,%10;" "psrlq $0x11,%%xmm2;" "xor %4,%11;" "xor %9,%12;" "psrlq $0x13,%%xmm3;" "xor %7,%10;" "and %7,%12;" "ror $0xb,%11;" "psrld $0xa,%%xmm7;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "pxor %%xmm3,%%xmm2;" "ror $0x2,%11;" "add %10,%12;" "add 12+%16,%12;" "pxor %%xmm2,%%xmm7;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "pshufb %%xmm11,%%xmm7;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "paddd %%xmm0,%%xmm7;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "sub $0x1,%1;" "jne Lloop1_%=;" "mov $0x2,%1;" "Lloop2_%=:" "paddd 0x0(%13),%%xmm4;" "movdqa %%xmm4,%16;" "mov %k2,%10;" "ror $0xe,%10;" "mov %3,%11;" "xor %k2,%10;" "ror $0x9,%11;" "mov %7,%12;" "xor %3,%11;" "ror $0x5,%10;" "xor %8,%12;" "xor %k2,%10;" "ror $0xb,%11;" "and %k2,%12;" "xor %3,%11;" "ror $0x6,%10;" "xor %8,%12;" "add %10,%12;" "ror $0x2,%11;" "add %16,%12;" "mov %3,%10;" "add %12,%9;" "mov %3,%12;" "or %5,%10;" "add %9,%6;" "and %5,%12;" "and %4,%10;" "add %11,%9;" "or %12,%10;" "add %10,%9;" "mov %6,%10;" "ror $0xe,%10;" "mov %9,%11;" "xor %6,%10;" "ror $0x9,%11;" "mov %k2,%12;" "xor %9,%11;" "ror $0x5,%10;" "xor %7,%12;" "xor %6,%10;" "ror $0xb,%11;" "and %6,%12;" "xor %9,%11;" "ror $0x6,%10;" "xor %7,%12;" "add %10,%12;" "ror $0x2,%11;" "add 4+%16,%12;" "mov %9,%10;" "add %12,%8;" "mov %9,%12;" "or %4,%10;" "add %8,%5;" "and %4,%12;" "and %3,%10;" "add %11,%8;" "or %12,%10;" "add %10,%8;" "mov %5,%10;" "ror $0xe,%10;" "mov %8,%11;" "xor %5,%10;" "ror $0x9,%11;" "mov %6,%12;" "xor %8,%11;" "ror $0x5,%10;" "xor %k2,%12;" "xor %5,%10;" "ror $0xb,%11;" "and %5,%12;" "xor %8,%11;" "ror $0x6,%10;" "xor %k2,%12;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "mov %8,%10;" "add %12,%7;" "mov %8,%12;" "or %3,%10;" "add %7,%4;" "and %3,%12;" "and %9,%10;" "add %11,%7;" "or %12,%10;" "add %10,%7;" "mov %4,%10;" "ror $0xe,%10;" "mov %7,%11;" "xor %4,%10;" "ror $0x9,%11;" "mov %5,%12;" "xor %7,%11;" "ror $0x5,%10;" "xor %6,%12;" "xor %4,%10;" "ror $0xb,%11;" "and %4,%12;" "xor %7,%11;" "ror $0x6,%10;" "xor %6,%12;" "add %10,%12;" "ror $0x2,%11;" "add 12+%16,%12;" "mov %7,%10;" "add %12,%k2;" "mov %7,%12;" "or %9,%10;" "add %k2,%3;" "and %9,%12;" "and %8,%10;" "add %11,%k2;" "or %12,%10;" "add %10,%k2;" "paddd 0x10(%13),%%xmm5;" "movdqa %%xmm5,%16;" "add $0x20,%13;" "mov %3,%10;" "ror $0xe,%10;" "mov %k2,%11;" "xor %3,%10;" "ror $0x9,%11;" "mov %4,%12;" "xor %k2,%11;" "ror $0x5,%10;" "xor %5,%12;" "xor %3,%10;" "ror $0xb,%11;" "and %3,%12;" "xor %k2,%11;" "ror $0x6,%10;" "xor %5,%12;" "add %10,%12;" "ror $0x2,%11;" "add %16,%12;" "mov %k2,%10;" "add %12,%6;" "mov %k2,%12;" "or %8,%10;" "add %6,%9;" "and %8,%12;" "and %7,%10;" "add %11,%6;" "or %12,%10;" "add %10,%6;" "mov %9,%10;" "ror $0xe,%10;" "mov %6,%11;" "xor %9,%10;" "ror $0x9,%11;" "mov %3,%12;" "xor %6,%11;" "ror $0x5,%10;" "xor %4,%12;" "xor %9,%10;" "ror $0xb,%11;" "and %9,%12;" "xor %6,%11;" "ror $0x6,%10;" "xor %4,%12;" "add %10,%12;" "ror $0x2,%11;" "add 4+%16,%12;" "mov %6,%10;" "add %12,%5;" "mov %6,%12;" "or %7,%10;" "add %5,%8;" "and %7,%12;" "and %k2,%10;" "add %11,%5;" "or %12,%10;" "add %10,%5;" "mov %8,%10;" "ror $0xe,%10;" "mov %5,%11;" "xor %8,%10;" "ror $0x9,%11;" "mov %9,%12;" "xor %5,%11;" "ror $0x5,%10;" "xor %3,%12;" "xor %8,%10;" "ror $0xb,%11;" "and %8,%12;" "xor %5,%11;" "ror $0x6,%10;" "xor %3,%12;" "add %10,%12;" "ror $0x2,%11;" "add 8+%16,%12;" "mov %5,%10;" "add %12,%4;" "mov %5,%12;" "or %k2,%10;" "add %4,%7;" "and %k2,%12;" "and %6,%10;" "add %11,%4;" "or %12,%10;" "add %10,%4;" "mov %7,%10;" "ror $0xe,%10;" "mov %4,%11;" "xor %7,%10;" "ror $0x9,%11;" "mov %8,%12;" "xor %4,%11;" "ror $0x5,%10;" "xor %9,%12;" "xor %7,%10;" "ror $0xb,%11;" "and %7,%12;" "xor %4,%11;" "ror $0x6,%10;" "xor %9,%12;" "add %10,%12;" "ror $0x2,%11;" "add 12+%16,%12;" "mov %4,%10;" "add %12,%3;" "mov %4,%12;" "or %6,%10;" "add %3,%k2;" "and %6,%12;" "and %5,%10;" "add %11,%3;" "or %12,%10;" "add %10,%3;" "movdqa %%xmm6,%%xmm4;" "movdqa %%xmm7,%%xmm5;" "sub $0x1,%1;" "jne Lloop2_%=;" "add (%0),%3;" "mov %3,(%0);" "add 0x4(%0),%4;" "mov %4,0x4(%0);" "add 0x8(%0),%5;" "mov %5,0x8(%0);" "add 0xc(%0),%6;" "mov %6,0xc(%0);" "add 0x10(%0),%k2;" "mov %k2,0x10(%0);" "add 0x14(%0),%7;" "mov %7,0x14(%0);" "add 0x18(%0),%8;" "mov %8,0x18(%0);" "add 0x1c(%0),%9;" "mov %9,0x1c(%0);" "mov %15,%1;" "add $0x40,%1;" "cmp %14,%1;" "jne Lloop0_%=;" "Ldone_hash_%=:" : "+r"(s), "+r"(chunk), "+r"(blocks), "=r"(a), "=r"(b), "=r"(c), "=r"(d), /* e = chunk */ "=r"(f), "=r"(g), "=r"(h), "=r"(y0), "=r"(y1), "=r"(y2), "=r"(tbl), "+m"(inp_end), "+m"(inp), "+m"(xfer) : "m"(K256), "m"(FLIP_MASK), "m"(SHUF_00BA), "m"(SHUF_DC00) : "cc", "memory", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12"); } -} +} // namespace sha256_sse4 /* ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Copyright (c) 2012, Intel Corporation ; ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions are ; met: ; ; * Redistributions of source code must retain the above copyright ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; notice, this list of conditions and the following disclaimer in the ; documentation and/or other materials provided with the ; distribution. ; ; * Neither the name of the Intel Corporation nor the names of its ; contributors may be used to endorse or promote products derived from ; this software without specific prior written permission. ; ; ; THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR ; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Example YASM command lines: ; Windows: yasm -Xvc -f x64 -rnasm -pnasm -o sha256_sse4.obj -g cv8 sha256_sse4.asm ; Linux: yasm -f x64 -f elf64 -X gnu -g dwarf2 -D LINUX -o sha256_sse4.o sha256_sse4.asm ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This code is described in an Intel White-Paper: ; "Fast SHA-256 Implementations on Intel Architecture Processors" ; ; To find it, surf to http://www.intel.com/p/en_US/embedded ; and search for that title. ; The paper is expected to be released roughly at the end of April, 2012 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This code schedules 1 blocks at a time, with 4 lanes per block ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %define MOVDQ movdqu ;; assume buffers not aligned ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Define Macros ; addm [mem], reg ; Add reg to mem using reg-mem add and store %macro addm 2 add %2, %1 mov %1, %2 %endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; COPY_XMM_AND_BSWAP xmm, [mem], byte_flip_mask ; Load xmm with mem and byte swap each dword %macro COPY_XMM_AND_BSWAP 3 MOVDQ %1, %2 pshufb %1, %3 %endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; %define X0 xmm4 %define X1 xmm5 %define X2 xmm6 %define X3 xmm7 %define XTMP0 xmm0 %define XTMP1 xmm1 %define XTMP2 xmm2 %define XTMP3 xmm3 %define XTMP4 xmm8 %define XFER xmm9 %define SHUF_00BA xmm10 ; shuffle xBxA -> 00BA %define SHUF_DC00 xmm11 ; shuffle xDxC -> DC00 %define BYTE_FLIP_MASK xmm12 %ifdef LINUX %define NUM_BLKS rdx ; 3rd arg %define CTX rsi ; 2nd arg %define INP rdi ; 1st arg %define SRND rdi ; clobbers INP %define c ecx %define d r8d %define e edx %else %define NUM_BLKS r8 ; 3rd arg %define CTX rdx ; 2nd arg %define INP rcx ; 1st arg %define SRND rcx ; clobbers INP %define c edi %define d esi %define e r8d %endif %define TBL rbp %define a eax %define b ebx %define f r9d %define g r10d %define h r11d %define y0 r13d %define y1 r14d %define y2 r15d _INP_END_SIZE equ 8 _INP_SIZE equ 8 _XFER_SIZE equ 8 %ifdef LINUX _XMM_SAVE_SIZE equ 0 %else _XMM_SAVE_SIZE equ 7*16 %endif ; STACK_SIZE plus pushes must be an odd multiple of 8 _ALIGN_SIZE equ 8 _INP_END equ 0 _INP equ _INP_END + _INP_END_SIZE _XFER equ _INP + _INP_SIZE _XMM_SAVE equ _XFER + _XFER_SIZE + _ALIGN_SIZE STACK_SIZE equ _XMM_SAVE + _XMM_SAVE_SIZE ; rotate_Xs ; Rotate values of symbols X0...X3 %macro rotate_Xs 0 %xdefine X_ X0 %xdefine X0 X1 %xdefine X1 X2 %xdefine X2 X3 %xdefine X3 X_ %endm ; ROTATE_ARGS ; Rotate values of symbols a...h %macro ROTATE_ARGS 0 %xdefine TMP_ h %xdefine h g %xdefine g f %xdefine f e %xdefine e d %xdefine d c %xdefine c b %xdefine b a %xdefine a TMP_ %endm %macro FOUR_ROUNDS_AND_SCHED 0 ;; compute s0 four at a time and s1 two at a time ;; compute W[-16] + W[-7] 4 at a time movdqa XTMP0, X3 mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a palignr XTMP0, X2, 4 ; XTMP0 = W[-7] ror y1, (22-13) ; y1 = a >> (22-13) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) movdqa XTMP1, X1 xor y1, a ; y1 = a ^ (a >> (22-13) xor y2, g ; y2 = f^g paddd XTMP0, X0 ; XTMP0 = W[-7] + W[-16] xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) ;; compute s0 palignr XTMP1, X0, 4 ; XTMP1 = W[-15] xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g movdqa XTMP2, XTMP1 ; XTMP2 = W[-15] ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 0*4] ; y2 = k + w + S1 + CH movdqa XTMP3, XTMP1 ; XTMP3 = W[-15] mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pslld XTMP1, (32-7) or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c psrld XTMP2, 7 and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 por XTMP1, XTMP2 ; XTMP1 = W[-15] ror 7 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP2, XTMP3 ; XTMP2 = W[-15] mov y0, e ; y0 = e mov y1, a ; y1 = a movdqa XTMP4, XTMP3 ; XTMP4 = W[-15] ror y0, (25-11) ; y0 = e >> (25-11) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y1, (22-13) ; y1 = a >> (22-13) pslld XTMP3, (32-18) xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) xor y2, g ; y2 = f^g psrld XTMP2, 18 ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) pxor XTMP1, XTMP3 xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) xor y2, g ; y2 = CH = ((f^g)&e)^g psrld XTMP4, 3 ; XTMP4 = W[-15] >> 3 add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 1*4] ; y2 = k + w + S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) pxor XTMP1, XTMP2 ; XTMP1 = W[-15] ror 7 ^ W[-15] ror 18 mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pxor XTMP1, XTMP4 ; XTMP1 = s0 or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c ;; compute low s1 pshufd XTMP2, X3, 11111010b ; XTMP2 = W[-2] {BBAA} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 paddd XTMP0, XTMP1 ; XTMP0 = W[-16] + W[-7] + s0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP3, XTMP2 ; XTMP3 = W[-2] {BBAA} mov y0, e ; y0 = e mov y1, a ; y1 = a ror y0, (25-11) ; y0 = e >> (25-11) movdqa XTMP4, XTMP2 ; XTMP4 = W[-2] {BBAA} xor y0, e ; y0 = e ^ (e >> (25-11)) ror y1, (22-13) ; y1 = a >> (22-13) mov y2, f ; y2 = f xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) psrlq XTMP2, 17 ; XTMP2 = W[-2] ror 17 {xBxA} xor y2, g ; y2 = f^g psrlq XTMP3, 19 ; XTMP3 = W[-2] ror 19 {xBxA} xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e psrld XTMP4, 10 ; XTMP4 = W[-2] >> 10 {BBAA} ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) xor y2, g ; y2 = CH = ((f^g)&e)^g ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) pxor XTMP2, XTMP3 add y2, y0 ; y2 = S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, [rsp + _XFER + 2*4] ; y2 = k + w + S1 + CH pxor XTMP4, XTMP2 ; XTMP4 = s1 {xBxA} mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pshufb XTMP4, SHUF_00BA ; XTMP4 = s1 {00BA} or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c paddd XTMP0, XTMP4 ; XTMP0 = {..., ..., W[1], W[0]} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 ;; compute high s1 pshufd XTMP2, XTMP0, 01010000b ; XTMP2 = W[-2] {DDCC} or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS movdqa XTMP3, XTMP2 ; XTMP3 = W[-2] {DDCC} mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a movdqa X0, XTMP2 ; X0 = W[-2] {DDCC} ror y1, (22-13) ; y1 = a >> (22-13) xor y0, e ; y0 = e ^ (e >> (25-11)) mov y2, f ; y2 = f ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) psrlq XTMP2, 17 ; XTMP2 = W[-2] ror 17 {xDxC} xor y1, a ; y1 = a ^ (a >> (22-13) xor y2, g ; y2 = f^g psrlq XTMP3, 19 ; XTMP3 = W[-2] ror 19 {xDxC} xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) and y2, e ; y2 = (f^g)&e ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) psrld X0, 10 ; X0 = W[-2] >> 10 {DDCC} xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g pxor XTMP2, XTMP3 ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, y0 ; y2 = S1 + CH add y2, [rsp + _XFER + 3*4] ; y2 = k + w + S1 + CH pxor X0, XTMP2 ; X0 = s1 {xDxC} mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a pshufb X0, SHUF_DC00 ; X0 = s1 {DC00} or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c paddd X0, XTMP0 ; X0 = {W[3], W[2], W[1], W[0]} and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS rotate_Xs %endm ;; input is [rsp + _XFER + %1 * 4] %macro DO_ROUND 1 mov y0, e ; y0 = e ror y0, (25-11) ; y0 = e >> (25-11) mov y1, a ; y1 = a xor y0, e ; y0 = e ^ (e >> (25-11)) ror y1, (22-13) ; y1 = a >> (22-13) mov y2, f ; y2 = f xor y1, a ; y1 = a ^ (a >> (22-13) ror y0, (11-6) ; y0 = (e >> (11-6)) ^ (e >> (25-6)) xor y2, g ; y2 = f^g xor y0, e ; y0 = e ^ (e >> (11-6)) ^ (e >> (25-6)) ror y1, (13-2) ; y1 = (a >> (13-2)) ^ (a >> (22-2)) and y2, e ; y2 = (f^g)&e xor y1, a ; y1 = a ^ (a >> (13-2)) ^ (a >> (22-2)) ror y0, 6 ; y0 = S1 = (e>>6) & (e>>11) ^ (e>>25) xor y2, g ; y2 = CH = ((f^g)&e)^g add y2, y0 ; y2 = S1 + CH ror y1, 2 ; y1 = S0 = (a>>2) ^ (a>>13) ^ (a>>22) add y2, [rsp + _XFER + %1 * 4] ; y2 = k + w + S1 + CH mov y0, a ; y0 = a add h, y2 ; h = h + S1 + CH + k + w mov y2, a ; y2 = a or y0, c ; y0 = a|c add d, h ; d = d + h + S1 + CH + k + w and y2, c ; y2 = a&c and y0, b ; y0 = (a|c)&b add h, y1 ; h = h + S1 + CH + k + w + S0 or y0, y2 ; y0 = MAJ = (a|c)&b)|(a&c) add h, y0 ; h = h + S1 + CH + k + w + S0 + MAJ ROTATE_ARGS %endm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; void sha256_sse4(void *input_data, UINT32 digest[8], UINT64 num_blks) ;; arg 1 : pointer to input data ;; arg 2 : pointer to digest ;; arg 3 : Num blocks section .text global sha256_sse4 align 32 sha256_sse4: push rbx %ifndef LINUX push rsi push rdi %endif push rbp push r13 push r14 push r15 sub rsp,STACK_SIZE %ifndef LINUX movdqa [rsp + _XMM_SAVE + 0*16],xmm6 movdqa [rsp + _XMM_SAVE + 1*16],xmm7 movdqa [rsp + _XMM_SAVE + 2*16],xmm8 movdqa [rsp + _XMM_SAVE + 3*16],xmm9 movdqa [rsp + _XMM_SAVE + 4*16],xmm10 movdqa [rsp + _XMM_SAVE + 5*16],xmm11 movdqa [rsp + _XMM_SAVE + 6*16],xmm12 %endif shl NUM_BLKS, 6 ; convert to bytes jz done_hash add NUM_BLKS, INP ; pointer to end of data mov [rsp + _INP_END], NUM_BLKS ;; load initial digest mov a,[4*0 + CTX] mov b,[4*1 + CTX] mov c,[4*2 + CTX] mov d,[4*3 + CTX] mov e,[4*4 + CTX] mov f,[4*5 + CTX] mov g,[4*6 + CTX] mov h,[4*7 + CTX] movdqa BYTE_FLIP_MASK, [PSHUFFLE_BYTE_FLIP_MASK wrt rip] movdqa SHUF_00BA, [_SHUF_00BA wrt rip] movdqa SHUF_DC00, [_SHUF_DC00 wrt rip] loop0: lea TBL,[K256 wrt rip] ;; byte swap first 16 dwords COPY_XMM_AND_BSWAP X0, [INP + 0*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X1, [INP + 1*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X2, [INP + 2*16], BYTE_FLIP_MASK COPY_XMM_AND_BSWAP X3, [INP + 3*16], BYTE_FLIP_MASK mov [rsp + _INP], INP ;; schedule 48 input dwords, by doing 3 rounds of 16 each mov SRND, 3 align 16 loop1: movdqa XFER, [TBL + 0*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 1*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 2*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER FOUR_ROUNDS_AND_SCHED movdqa XFER, [TBL + 3*16] paddd XFER, X0 movdqa [rsp + _XFER], XFER add TBL, 4*16 FOUR_ROUNDS_AND_SCHED sub SRND, 1 jne loop1 mov SRND, 2 loop2: paddd X0, [TBL + 0*16] movdqa [rsp + _XFER], X0 DO_ROUND 0 DO_ROUND 1 DO_ROUND 2 DO_ROUND 3 paddd X1, [TBL + 1*16] movdqa [rsp + _XFER], X1 add TBL, 2*16 DO_ROUND 0 DO_ROUND 1 DO_ROUND 2 DO_ROUND 3 movdqa X0, X2 movdqa X1, X3 sub SRND, 1 jne loop2 addm [4*0 + CTX],a addm [4*1 + CTX],b addm [4*2 + CTX],c addm [4*3 + CTX],d addm [4*4 + CTX],e addm [4*5 + CTX],f addm [4*6 + CTX],g addm [4*7 + CTX],h mov INP, [rsp + _INP] add INP, 64 cmp INP, [rsp + _INP_END] jne loop0 done_hash: %ifndef LINUX movdqa xmm6,[rsp + _XMM_SAVE + 0*16] movdqa xmm7,[rsp + _XMM_SAVE + 1*16] movdqa xmm8,[rsp + _XMM_SAVE + 2*16] movdqa xmm9,[rsp + _XMM_SAVE + 3*16] movdqa xmm10,[rsp + _XMM_SAVE + 4*16] movdqa xmm11,[rsp + _XMM_SAVE + 5*16] movdqa xmm12,[rsp + _XMM_SAVE + 6*16] %endif add rsp, STACK_SIZE pop r15 pop r14 pop r13 pop rbp %ifndef LINUX pop rdi pop rsi %endif pop rbx ret section .data align 64 K256: dd 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 dd 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 dd 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 dd 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 dd 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc dd 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da dd 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 dd 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 dd 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 dd 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 dd 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 dd 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 dd 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 dd 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 dd 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 dd 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 PSHUFFLE_BYTE_FLIP_MASK: ddq 0x0c0d0e0f08090a0b0405060700010203 ; shuffle xBxA -> 00BA _SHUF_00BA: ddq 0xFFFFFFFFFFFFFFFF0b0a090803020100 ; shuffle xDxC -> DC00 _SHUF_DC00: ddq 0x0b0a090803020100FFFFFFFFFFFFFFFF */ #endif diff --git a/src/crypto/sha256_sse41.cpp b/src/crypto/sha256_sse41.cpp index ab30fab3b..bea2b3675 100644 --- a/src/crypto/sha256_sse41.cpp +++ b/src/crypto/sha256_sse41.cpp @@ -1,444 +1,444 @@ #ifdef ENABLE_SSE41 #include #include #include #include namespace sha256d64_sse41 { namespace { __m128i inline K(uint32_t x) { return _mm_set1_epi32(x); } __m128i inline Add(__m128i x, __m128i y) { return _mm_add_epi32(x, y); } __m128i inline Add(__m128i x, __m128i y, __m128i z) { return Add(Add(x, y), z); } __m128i inline Add(__m128i x, __m128i y, __m128i z, __m128i w) { return Add(Add(x, y), Add(z, w)); } __m128i inline Add(__m128i x, __m128i y, __m128i z, __m128i w, __m128i v) { return Add(Add(x, y, z), Add(w, v)); } __m128i inline Inc(__m128i &x, __m128i y) { x = Add(x, y); return x; } __m128i inline Inc(__m128i &x, __m128i y, __m128i z) { x = Add(x, y, z); return x; } __m128i inline Inc(__m128i &x, __m128i y, __m128i z, __m128i w) { x = Add(x, y, z, w); return x; } __m128i inline Xor(__m128i x, __m128i y) { return _mm_xor_si128(x, y); } __m128i inline Xor(__m128i x, __m128i y, __m128i z) { return Xor(Xor(x, y), z); } __m128i inline Or(__m128i x, __m128i y) { return _mm_or_si128(x, y); } __m128i inline And(__m128i x, __m128i y) { return _mm_and_si128(x, y); } __m128i inline ShR(__m128i x, int n) { return _mm_srli_epi32(x, n); } __m128i inline ShL(__m128i x, int n) { return _mm_slli_epi32(x, n); } __m128i inline Ch(__m128i x, __m128i y, __m128i z) { return Xor(z, And(x, Xor(y, z))); } __m128i inline Maj(__m128i x, __m128i y, __m128i z) { return Or(And(x, y), And(z, Or(x, y))); } __m128i inline Sigma0(__m128i x) { return Xor(Or(ShR(x, 2), ShL(x, 30)), Or(ShR(x, 13), ShL(x, 19)), Or(ShR(x, 22), ShL(x, 10))); } __m128i inline Sigma1(__m128i x) { return Xor(Or(ShR(x, 6), ShL(x, 26)), Or(ShR(x, 11), ShL(x, 21)), Or(ShR(x, 25), ShL(x, 7))); } __m128i inline sigma0(__m128i x) { return Xor(Or(ShR(x, 7), ShL(x, 25)), Or(ShR(x, 18), ShL(x, 14)), ShR(x, 3)); } __m128i inline sigma1(__m128i x) { return Xor(Or(ShR(x, 17), ShL(x, 15)), Or(ShR(x, 19), ShL(x, 13)), ShR(x, 10)); } /** One round of SHA-256. */ inline void __attribute__((always_inline)) Round(__m128i a, __m128i b, __m128i c, __m128i &d, __m128i e, __m128i f, __m128i g, __m128i &h, __m128i k) { __m128i t1 = Add(h, Sigma1(e), Ch(e, f, g), k); __m128i t2 = Add(Sigma0(a), Maj(a, b, c)); d = Add(d, t1); h = Add(t1, t2); } __m128i inline Read4(const uint8_t *chunk, int offset) { __m128i ret = _mm_set_epi32( ReadLE32(chunk + 0 + offset), ReadLE32(chunk + 64 + offset), ReadLE32(chunk + 128 + offset), ReadLE32(chunk + 192 + offset)); return _mm_shuffle_epi8(ret, _mm_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); } inline void Write4(uint8_t *out, int offset, __m128i v) { v = _mm_shuffle_epi8(v, _mm_set_epi32(0x0C0D0E0FUL, 0x08090A0BUL, 0x04050607UL, 0x00010203UL)); WriteLE32(out + 0 + offset, _mm_extract_epi32(v, 3)); WriteLE32(out + 32 + offset, _mm_extract_epi32(v, 2)); WriteLE32(out + 64 + offset, _mm_extract_epi32(v, 1)); WriteLE32(out + 96 + offset, _mm_extract_epi32(v, 0)); } -} +} // namespace void Transform_4way(uint8_t *out, const uint8_t *in) { // Transform 1 __m128i a = K(0x6a09e667ul); __m128i b = K(0xbb67ae85ul); __m128i c = K(0x3c6ef372ul); __m128i d = K(0xa54ff53aul); __m128i e = K(0x510e527ful); __m128i f = K(0x9b05688cul); __m128i g = K(0x1f83d9abul); __m128i h = K(0x5be0cd19ul); __m128i w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0 = Read4(in, 0))); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1 = Read4(in, 4))); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2 = Read4(in, 8))); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3 = Read4(in, 12))); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4 = Read4(in, 16))); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5 = Read4(in, 20))); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6 = Read4(in, 24))); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7 = Read4(in, 28))); Round(a, b, c, d, e, f, g, h, Add(K(0xd807aa98ul), w8 = Read4(in, 32))); Round(h, a, b, c, d, e, f, g, Add(K(0x12835b01ul), w9 = Read4(in, 36))); Round(g, h, a, b, c, d, e, f, Add(K(0x243185beul), w10 = Read4(in, 40))); Round(f, g, h, a, b, c, d, e, Add(K(0x550c7dc3ul), w11 = Read4(in, 44))); Round(e, f, g, h, a, b, c, d, Add(K(0x72be5d74ul), w12 = Read4(in, 48))); Round(d, e, f, g, h, a, b, c, Add(K(0x80deb1feul), w13 = Read4(in, 52))); Round(c, d, e, f, g, h, a, b, Add(K(0x9bdc06a7ul), w14 = Read4(in, 56))); Round(b, c, d, e, f, g, h, a, Add(K(0xc19bf174ul), w15 = Read4(in, 60))); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); a = Add(a, K(0x6a09e667ul)); b = Add(b, K(0xbb67ae85ul)); c = Add(c, K(0x3c6ef372ul)); d = Add(d, K(0xa54ff53aul)); e = Add(e, K(0x510e527ful)); f = Add(f, K(0x9b05688cul)); g = Add(g, K(0x1f83d9abul)); h = Add(h, K(0x5be0cd19ul)); __m128i t0 = a, t1 = b, t2 = c, t3 = d, t4 = e, t5 = f, t6 = g, t7 = h; // Transform 2 Round(a, b, c, d, e, f, g, h, K(0xc28a2f98ul)); Round(h, a, b, c, d, e, f, g, K(0x71374491ul)); Round(g, h, a, b, c, d, e, f, K(0xb5c0fbcful)); Round(f, g, h, a, b, c, d, e, K(0xe9b5dba5ul)); Round(e, f, g, h, a, b, c, d, K(0x3956c25bul)); Round(d, e, f, g, h, a, b, c, K(0x59f111f1ul)); Round(c, d, e, f, g, h, a, b, K(0x923f82a4ul)); Round(b, c, d, e, f, g, h, a, K(0xab1c5ed5ul)); Round(a, b, c, d, e, f, g, h, K(0xd807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf374ul)); Round(a, b, c, d, e, f, g, h, K(0x649b69c1ul)); Round(h, a, b, c, d, e, f, g, K(0xf0fe4786ul)); Round(g, h, a, b, c, d, e, f, K(0x0fe1edc6ul)); Round(f, g, h, a, b, c, d, e, K(0x240cf254ul)); Round(e, f, g, h, a, b, c, d, K(0x4fe9346ful)); Round(d, e, f, g, h, a, b, c, K(0x6cc984beul)); Round(c, d, e, f, g, h, a, b, K(0x61b9411eul)); Round(b, c, d, e, f, g, h, a, K(0x16f988faul)); Round(a, b, c, d, e, f, g, h, K(0xf2c65152ul)); Round(h, a, b, c, d, e, f, g, K(0xa88e5a6dul)); Round(g, h, a, b, c, d, e, f, K(0xb019fc65ul)); Round(f, g, h, a, b, c, d, e, K(0xb9d99ec7ul)); Round(e, f, g, h, a, b, c, d, K(0x9a1231c3ul)); Round(d, e, f, g, h, a, b, c, K(0xe70eeaa0ul)); Round(c, d, e, f, g, h, a, b, K(0xfdb1232bul)); Round(b, c, d, e, f, g, h, a, K(0xc7353eb0ul)); Round(a, b, c, d, e, f, g, h, K(0x3069bad5ul)); Round(h, a, b, c, d, e, f, g, K(0xcb976d5ful)); Round(g, h, a, b, c, d, e, f, K(0x5a0f118ful)); Round(f, g, h, a, b, c, d, e, K(0xdc1eeefdul)); Round(e, f, g, h, a, b, c, d, K(0x0a35b689ul)); Round(d, e, f, g, h, a, b, c, K(0xde0b7a04ul)); Round(c, d, e, f, g, h, a, b, K(0x58f4ca9dul)); Round(b, c, d, e, f, g, h, a, K(0xe15d5b16ul)); Round(a, b, c, d, e, f, g, h, K(0x007f3e86ul)); Round(h, a, b, c, d, e, f, g, K(0x37088980ul)); Round(g, h, a, b, c, d, e, f, K(0xa507ea32ul)); Round(f, g, h, a, b, c, d, e, K(0x6fab9537ul)); Round(e, f, g, h, a, b, c, d, K(0x17406110ul)); Round(d, e, f, g, h, a, b, c, K(0x0d8cd6f1ul)); Round(c, d, e, f, g, h, a, b, K(0xcdaa3b6dul)); Round(b, c, d, e, f, g, h, a, K(0xc0bbbe37ul)); Round(a, b, c, d, e, f, g, h, K(0x83613bdaul)); Round(h, a, b, c, d, e, f, g, K(0xdb48a363ul)); Round(g, h, a, b, c, d, e, f, K(0x0b02e931ul)); Round(f, g, h, a, b, c, d, e, K(0x6fd15ca7ul)); Round(e, f, g, h, a, b, c, d, K(0x521afacaul)); Round(d, e, f, g, h, a, b, c, K(0x31338431ul)); Round(c, d, e, f, g, h, a, b, K(0x6ed41a95ul)); Round(b, c, d, e, f, g, h, a, K(0x6d437890ul)); Round(a, b, c, d, e, f, g, h, K(0xc39c91f2ul)); Round(h, a, b, c, d, e, f, g, K(0x9eccabbdul)); Round(g, h, a, b, c, d, e, f, K(0xb5c9a0e6ul)); Round(f, g, h, a, b, c, d, e, K(0x532fb63cul)); Round(e, f, g, h, a, b, c, d, K(0xd2c741c6ul)); Round(d, e, f, g, h, a, b, c, K(0x07237ea3ul)); Round(c, d, e, f, g, h, a, b, K(0xa4954b68ul)); Round(b, c, d, e, f, g, h, a, K(0x4c191d76ul)); w0 = Add(t0, a); w1 = Add(t1, b); w2 = Add(t2, c); w3 = Add(t3, d); w4 = Add(t4, e); w5 = Add(t5, f); w6 = Add(t6, g); w7 = Add(t7, h); // Transform 3 a = K(0x6a09e667ul); b = K(0xbb67ae85ul); c = K(0x3c6ef372ul); d = K(0xa54ff53aul); e = K(0x510e527ful); f = K(0x9b05688cul); g = K(0x1f83d9abul); h = K(0x5be0cd19ul); Round(a, b, c, d, e, f, g, h, Add(K(0x428a2f98ul), w0)); Round(h, a, b, c, d, e, f, g, Add(K(0x71374491ul), w1)); Round(g, h, a, b, c, d, e, f, Add(K(0xb5c0fbcful), w2)); Round(f, g, h, a, b, c, d, e, Add(K(0xe9b5dba5ul), w3)); Round(e, f, g, h, a, b, c, d, Add(K(0x3956c25bul), w4)); Round(d, e, f, g, h, a, b, c, Add(K(0x59f111f1ul), w5)); Round(c, d, e, f, g, h, a, b, Add(K(0x923f82a4ul), w6)); Round(b, c, d, e, f, g, h, a, Add(K(0xab1c5ed5ul), w7)); Round(a, b, c, d, e, f, g, h, K(0x5807aa98ul)); Round(h, a, b, c, d, e, f, g, K(0x12835b01ul)); Round(g, h, a, b, c, d, e, f, K(0x243185beul)); Round(f, g, h, a, b, c, d, e, K(0x550c7dc3ul)); Round(e, f, g, h, a, b, c, d, K(0x72be5d74ul)); Round(d, e, f, g, h, a, b, c, K(0x80deb1feul)); Round(c, d, e, f, g, h, a, b, K(0x9bdc06a7ul)); Round(b, c, d, e, f, g, h, a, K(0xc19bf274ul)); Round(a, b, c, d, e, f, g, h, Add(K(0xe49b69c1ul), Inc(w0, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0xefbe4786ul), Inc(w1, K(0xa00000ul), sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x0fc19dc6ul), Inc(w2, sigma1(w0), sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x240ca1ccul), Inc(w3, sigma1(w1), sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x2de92c6ful), Inc(w4, sigma1(w2), sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4a7484aaul), Inc(w5, sigma1(w3), sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5cb0a9dcul), Inc(w6, sigma1(w4), K(0x100ul), sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x76f988daul), Inc(w7, sigma1(w5), w0, K(0x11002000ul)))); Round(a, b, c, d, e, f, g, h, Add(K(0x983e5152ul), w8 = Add(K(0x80000000ul), sigma1(w6), w1))); Round(h, a, b, c, d, e, f, g, Add(K(0xa831c66dul), w9 = Add(sigma1(w7), w2))); Round(g, h, a, b, c, d, e, f, Add(K(0xb00327c8ul), w10 = Add(sigma1(w8), w3))); Round(f, g, h, a, b, c, d, e, Add(K(0xbf597fc7ul), w11 = Add(sigma1(w9), w4))); Round(e, f, g, h, a, b, c, d, Add(K(0xc6e00bf3ul), w12 = Add(sigma1(w10), w5))); Round(d, e, f, g, h, a, b, c, Add(K(0xd5a79147ul), w13 = Add(sigma1(w11), w6))); Round(c, d, e, f, g, h, a, b, Add(K(0x06ca6351ul), w14 = Add(sigma1(w12), w7, K(0x400022ul)))); Round(b, c, d, e, f, g, h, a, Add(K(0x14292967ul), w15 = Add(K(0x100ul), sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x27b70a85ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x2e1b2138ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x4d2c6dfcul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x53380d13ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x650a7354ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x766a0abbul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x81c2c92eul), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x92722c85ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0xa2bfe8a1ul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0xa81a664bul), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0xc24b8b70ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0xc76c51a3ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0xd192e819ul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xd6990624ul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xf40e3585ul), Inc(w14, sigma1(w12), w7, sigma0(w15)))); Round(b, c, d, e, f, g, h, a, Add(K(0x106aa070ul), Inc(w15, sigma1(w13), w8, sigma0(w0)))); Round(a, b, c, d, e, f, g, h, Add(K(0x19a4c116ul), Inc(w0, sigma1(w14), w9, sigma0(w1)))); Round(h, a, b, c, d, e, f, g, Add(K(0x1e376c08ul), Inc(w1, sigma1(w15), w10, sigma0(w2)))); Round(g, h, a, b, c, d, e, f, Add(K(0x2748774cul), Inc(w2, sigma1(w0), w11, sigma0(w3)))); Round(f, g, h, a, b, c, d, e, Add(K(0x34b0bcb5ul), Inc(w3, sigma1(w1), w12, sigma0(w4)))); Round(e, f, g, h, a, b, c, d, Add(K(0x391c0cb3ul), Inc(w4, sigma1(w2), w13, sigma0(w5)))); Round(d, e, f, g, h, a, b, c, Add(K(0x4ed8aa4aul), Inc(w5, sigma1(w3), w14, sigma0(w6)))); Round(c, d, e, f, g, h, a, b, Add(K(0x5b9cca4ful), Inc(w6, sigma1(w4), w15, sigma0(w7)))); Round(b, c, d, e, f, g, h, a, Add(K(0x682e6ff3ul), Inc(w7, sigma1(w5), w0, sigma0(w8)))); Round(a, b, c, d, e, f, g, h, Add(K(0x748f82eeul), Inc(w8, sigma1(w6), w1, sigma0(w9)))); Round(h, a, b, c, d, e, f, g, Add(K(0x78a5636ful), Inc(w9, sigma1(w7), w2, sigma0(w10)))); Round(g, h, a, b, c, d, e, f, Add(K(0x84c87814ul), Inc(w10, sigma1(w8), w3, sigma0(w11)))); Round(f, g, h, a, b, c, d, e, Add(K(0x8cc70208ul), Inc(w11, sigma1(w9), w4, sigma0(w12)))); Round(e, f, g, h, a, b, c, d, Add(K(0x90befffaul), Inc(w12, sigma1(w10), w5, sigma0(w13)))); Round(d, e, f, g, h, a, b, c, Add(K(0xa4506cebul), Inc(w13, sigma1(w11), w6, sigma0(w14)))); Round(c, d, e, f, g, h, a, b, Add(K(0xbef9a3f7ul), w14, sigma1(w12), w7, sigma0(w15))); Round(b, c, d, e, f, g, h, a, Add(K(0xc67178f2ul), w15, sigma1(w13), w8, sigma0(w0))); // Output Write4(out, 0, Add(a, K(0x6a09e667ul))); Write4(out, 4, Add(b, K(0xbb67ae85ul))); Write4(out, 8, Add(c, K(0x3c6ef372ul))); Write4(out, 12, Add(d, K(0xa54ff53aul))); Write4(out, 16, Add(e, K(0x510e527ful))); Write4(out, 20, Add(f, K(0x9b05688cul))); Write4(out, 24, Add(g, K(0x1f83d9abul))); Write4(out, 28, Add(h, K(0x5be0cd19ul))); } -} +} // namespace sha256d64_sse41 #endif diff --git a/src/fs.cpp b/src/fs.cpp index 5e64572d1..ba9268583 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,18 +1,18 @@ // Copyright (c) 2017 The Bitcoin Core developers // Copyright (c) 2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include namespace fsbridge { FILE *fopen(const fs::path &p, const char *mode) { return ::fopen(p.string().c_str(), mode); } FILE *freopen(const fs::path &p, const char *mode, FILE *stream) { return ::freopen(p.string().c_str(), mode, stream); } -} // fsbridge +} // namespace fsbridge diff --git a/src/fs.h b/src/fs.h index 50d14f3ee..48e849d30 100644 --- a/src/fs.h +++ b/src/fs.h @@ -1,23 +1,23 @@ // Copyright (c) 2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_FS_H #define BITCOIN_FS_H #include #include #include #include /** Filesystem operations and types */ namespace fs = boost::filesystem; /** Bridge operations to C stdio */ namespace fsbridge { FILE *fopen(const fs::path &p, const char *mode); FILE *freopen(const fs::path &p, const char *mode, FILE *stream); -}; +}; // namespace fsbridge #endif // BITCOIN_FS_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index dc5796baf..45a5c0f9e 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1,4357 +1,4357 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(NDEBUG) #error "Bitcoin cannot be compiled without assertions." #endif // Used only to inform the wallet of when we last received a block. std::atomic nTimeBestReceived(0); struct IteratorComparator { template bool operator()(const I &a, const I &b) { return &(*a) < &(*b); } }; struct COrphanTx { // When modifying, adapt the copy of this definition in tests/DoS_tests. CTransactionRef tx; NodeId fromPeer; int64_t nTimeExpire; }; static CCriticalSection g_cs_orphans; std::map mapOrphanTransactions GUARDED_BY(g_cs_orphans); std::map::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); void EraseOrphansFor(NodeId peer); static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; static std::vector> vExtraTxnForCompact GUARDED_BY(g_cs_orphans); // SHA256("main address relay")[0:8] static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; /// Age after which a stale block will no longer be served if requested as /// protection against fingerprinting. Set to one month, denominated in seconds. static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; /// Age after which a block is considered historical for purposes of rate /// limiting block relay. Set to one week, denominated in seconds. static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; // How many non standard orphan do we consider from a node before ignoring it. static const uint32_t MAX_NON_STANDARD_ORPHAN_PER_NODE = 5; // Internal stuff namespace { /** Number of nodes with fSyncStarted. */ int nSyncStarted GUARDED_BY(cs_main) = 0; /** * Sources of received blocks, saved to be able to send them reject messages or * ban them when processing happens afterwards. * Set mapBlockSource[hash].second to false if the node should not be punished * if the block is invalid. */ std::map> mapBlockSource GUARDED_BY(cs_main); /** * Filter for transactions that were recently rejected by AcceptToMemoryPool. * These are not rerequested until the chain tip changes, at which point the * entire filter is reset. * * Without this filter we'd be re-requesting txs from each of our peers, * increasing bandwidth consumption considerably. For instance, with 100 peers, * half of which relay a tx we don't accept, that might be a 50x bandwidth * increase. A flooding attacker attempting to roll-over the filter using * minimum-sized, 60byte, transactions might manage to send 1000/sec if we have * fast peers, so we pick 120,000 to give our peers a two minute window to send * invs to us. * * Decreasing the false positive rate is fairly cheap, so we pick one in a * million to make it highly unlikely for users to have issues with this filter. * * Memory used: 1.3 MB */ std::unique_ptr recentRejects GUARDED_BY(cs_main); uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main); /** * Blocks that are in flight, and that are in the queue to be downloaded. */ struct QueuedBlock { uint256 hash; //!< Optional. const CBlockIndex *pindex; //!< Whether this block has validated headers at the time of request. bool fValidatedHeaders; //!< Optional, used for CMPCTBLOCK downloads std::unique_ptr partialBlock; }; std::map::iterator>> mapBlocksInFlight GUARDED_BY(cs_main); /** Stack of nodes which we have set to announce using compact blocks */ std::list lNodesAnnouncingHeaderAndIDs; /** Number of preferable block download peers. */ int nPreferredDownload GUARDED_BY(cs_main) = 0; /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0; /** Number of outbound peers with m_chain_sync.m_protect. */ int g_outbound_peers_with_protect_from_disconnect = 0; /** When our tip was last updated. */ std::atomic g_last_tip_update(0); /** Relay map. */ typedef std::map MapRelay; MapRelay mapRelay GUARDED_BY(cs_main); /** * Expiration-time ordered list of (expire time, relay map entry) pairs, * protected by cs_main). */ std::deque> vRelayExpiration GUARDED_BY(cs_main); } // namespace namespace { struct CBlockReject { uint8_t chRejectCode; std::string strRejectReason; uint256 hashBlock; }; /** * Maintain validation-specific state about nodes, protected by cs_main, instead * by CNode's own locks. This simplifies asynchronous operation, where * processing of incoming data is done after the ProcessMessage call returns, * and we're no longer holding the node's locks. */ struct CNodeState { //! The peer's address const CService address; //! Whether we have a fully established connection. bool fCurrentlyConnected; //! Accumulated misbehaviour score for this peer. int nMisbehavior; //! Whether this peer should be disconnected and banned (unless //! whitelisted). bool fShouldBan; //! String name of this peer (debugging/logging purposes). const std::string name; //! List of asynchronously-determined block rejections to notify this peer //! about. std::vector rejects; //! The best known block we know this peer has announced. const CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; //! The last full block we both have. const CBlockIndex *pindexLastCommonBlock; //! The best header we have sent our peer. const CBlockIndex *pindexBestHeaderSent; //! Length of current-streak of unconnecting headers announcements int nUnconnectingHeaders; //! Whether we've started headers synchronization with this peer. bool fSyncStarted; //! When to potentially disconnect peer for stalling headers download int64_t nHeadersSyncTimeout; //! Since when we're stalling block download progress (in microseconds), or //! 0. int64_t nStallingSince; std::list vBlocksInFlight; //! When the first entry in vBlocksInFlight started downloading. Don't care //! when vBlocksInFlight is empty. int64_t nDownloadingSince; int nBlocksInFlight; int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; //! Whether this peer wants invs or headers (when possible) for block //! announcements. bool fPreferHeaders; //! Whether this peer wants invs or cmpctblocks (when possible) for block //! announcements. bool fPreferHeaderAndIDs; /** * Whether this peer will send us cmpctblocks if we request them. * This is not used to gate request logic, as we really only care about * fSupportsDesiredCmpctVersion, but is used as a flag to "lock in" the * version of compact blocks we send. */ bool fProvidesHeaderAndIDs; /** * If we've announced NODE_WITNESS to this peer: whether the peer sends * witnesses in cmpctblocks/blocktxns, otherwise: whether this peer sends * non-witnesses in cmpctblocks/blocktxns. */ bool fSupportsDesiredCmpctVersion; /** * State used to enforce CHAIN_SYNC_TIMEOUT * Only in effect for outbound, non-manual connections, * with m_protect == false * Algorithm: if a peer's best known block has less work than our tip, set a * timeout CHAIN_SYNC_TIMEOUT seconds in the future: * - If at timeout their best known block now has more work than our tip * when the timeout was set, then either reset the timeout or clear it * (after comparing against our current tip's work) * - If at timeout their best known block still has less work than our tip * did when the timeout was set, then send a getheaders message, and set a * shorter timeout, HEADERS_RESPONSE_TIME seconds in future. If their best * known block is still behind when that new timeout is reached, disconnect. */ struct ChainSyncTimeoutState { //! A timeout used for checking whether our peer has sufficiently //! synced. int64_t m_timeout; //! A header with the work we require on our peer's chain. const CBlockIndex *m_work_header; //! After timeout is reached, set to true after sending getheaders. bool m_sent_getheaders; //! Whether this peer is protected from disconnection due to a bad/slow //! chain. bool m_protect; }; ChainSyncTimeoutState m_chain_sync; //! Time of last new block announcement int64_t m_last_block_announcement; CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) { fCurrentlyConnected = false; nMisbehavior = 0; fShouldBan = false; pindexBestKnownBlock = nullptr; hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = nullptr; pindexBestHeaderSent = nullptr; nUnconnectingHeaders = 0; fSyncStarted = false; nHeadersSyncTimeout = 0; nStallingSince = 0; nDownloadingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; fPreferHeaders = false; fPreferHeaderAndIDs = false; fProvidesHeaderAndIDs = false; fSupportsDesiredCmpctVersion = false; m_chain_sync = {0, nullptr, false, false}; m_last_block_announcement = 0; } }; /** Map maintaining per-node state. */ static std::map mapNodeState GUARDED_BY(cs_main); static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::map::iterator it = mapNodeState.find(pnode); if (it == mapNodeState.end()) { return nullptr; } return &it->second; } static void UpdatePreferredDownload(CNode *node, CNodeState *state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { nPreferredDownload -= state->fPreferredDownload; // Whether this node should be marked as a preferred download node. state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; nPreferredDownload += state->fPreferredDownload; } static void PushNodeVersion(const Config &config, CNode *pnode, CConnman *connman, int64_t nTime) { ServiceFlags nLocalNodeServices = pnode->GetLocalServices(); uint64_t nonce = pnode->GetLocalNonce(); int nNodeStartingHeight = pnode->GetMyStartingHeight(); NodeId nodeid = pnode->GetId(); CAddress addr = pnode->addr; CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); CAddress addrMe = CAddress(CService(), nLocalNodeServices); connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::VERSION, PROTOCOL_VERSION, uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, nonce, userAgent(config), nNodeStartingHeight, ::fRelayTxes)); if (fLogIPs) { LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, " "peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid); } else { LogPrint( BCLog::NET, "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), nodeid); } LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } // Returns a bool indicating whether we requested this block. // Also used if a block was /not/ received and timed out or started with another // peer. static bool MarkBlockAsReceived(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::map::iterator>>::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); assert(state != nullptr); state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { // Last validated block on the queue was received. nPeersWithValidatedDownloads--; } if (state->vBlocksInFlight.begin() == itInFlight->second.second) { // First block on the queue was received, update the start download // time for the next one state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); } state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); return true; } return false; } // returns false, still setting pit, if the block was already in flight from the // same peer // pit will only be valid as long as the same cs_main lock is being held. static bool MarkBlockAsInFlight(const Config &config, NodeId nodeid, const uint256 &hash, const Consensus::Params &consensusParams, const CBlockIndex *pindex = nullptr, std::list::iterator **pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { CNodeState *state = State(nodeid); assert(state != nullptr); // Short-circuit most stuff in case its from the same node. std::map::iterator>>::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end() && itInFlight->second.first == nodeid) { if (pit) { *pit = &itInFlight->second.second; } return false; } // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); std::list::iterator it = state->vBlocksInFlight.insert( state->vBlocksInFlight.end(), {hash, pindex, pindex != nullptr, std::unique_ptr( pit ? new PartiallyDownloadedBlock(config, &g_mempool) : nullptr)}); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += it->fValidatedHeaders; if (state->nBlocksInFlight == 1) { // We're starting a block download (batch) from this peer. state->nDownloadingSince = GetTimeMicros(); } if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) { nPeersWithValidatedDownloads++; } itInFlight = mapBlocksInFlight .insert(std::make_pair(hash, std::make_pair(nodeid, it))) .first; if (pit) { *pit = &itInFlight->second.second; } return true; } /** Check whether the last unknown block a peer advertised is not yet known. */ static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { CNodeState *state = State(nodeid); assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { const CBlockIndex *pindex = LookupBlockIndex(state->hashLastUnknownBlock); if (pindex && pindex->nChainWork > 0) { if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { state->pindexBestKnownBlock = pindex; } state->hashLastUnknownBlock.SetNull(); } } } /** Update tracking information about which blocks a peer is assumed to have. */ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { CNodeState *state = State(nodeid); assert(state != nullptr); ProcessBlockAvailability(nodeid); const CBlockIndex *pindex = LookupBlockIndex(hash); if (pindex && pindex->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { state->pindexBestKnownBlock = pindex; } } else { // An unknown block was announced; just assume that the latest one is // the best one. state->hashLastUnknownBlock = hash; } } /** * When a peer sends us a valid block, instruct it to announce blocks to us * using CMPCTBLOCK if possible by adding its nodeid to the end of * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by * removing the first element if necessary. */ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman *connman) { AssertLockHeld(cs_main); CNodeState *nodestate = State(nodeid); if (!nodestate) { LogPrint(BCLog::NET, "node state unavailable: peer=%d\n", nodeid); return; } if (!nodestate->fProvidesHeaderAndIDs) { return; } for (std::list::iterator it = lNodesAnnouncingHeaderAndIDs.begin(); it != lNodesAnnouncingHeaderAndIDs.end(); it++) { if (*it == nodeid) { lNodesAnnouncingHeaderAndIDs.erase(it); lNodesAnnouncingHeaderAndIDs.push_back(nodeid); return; } } connman->ForNode(nodeid, [&connman](CNode *pfrom) { AssertLockHeld(cs_main); uint64_t nCMPCTBLOCKVersion = 1; if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { // As per BIP152, we only get 3 of our peers to announce // blocks using compact encodings. connman->ForNode( lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode *pnodeStop) { AssertLockHeld(cs_main); connman->PushMessage( pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()) .Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()) .Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); } static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (g_last_tip_update == 0) { g_last_tip_update = GetTime(); } return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty(); } static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; } static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) { return true; } if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight)) { return true; } return false; } /** * Update pindexLastCommonBlock and add not-in-flight missing successors to * vBlocks, until it has at most count entries. */ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector &vBlocks, NodeId &nodeStaller, const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (count == 0) { return; } vBlocks.reserve(vBlocks.size() + count); CNodeState *state = State(nodeid); assert(state != nullptr); // Make sure pindexBestKnownBlock is up to date, we'll need it. ProcessBlockAvailability(nodeid); if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { // This peer has nothing interesting. return; } if (state->pindexLastCommonBlock == nullptr) { // Bootstrap quickly by guessing a parent of our best tip is the forking // point. Guessing wrong in either direction is not a problem. state->pindexLastCommonBlock = chainActive[std::min( state->pindexBestKnownBlock->nHeight, chainActive.Height())]; } // If the peer reorganized, our previous pindexLastCommonBlock may not be an // ancestor of its current tip anymore. Go back enough to fix that. state->pindexLastCommonBlock = LastCommonAncestor( state->pindexLastCommonBlock, state->pindexBestKnownBlock); if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) { return; } std::vector vToFetch; const CBlockIndex *pindexWalk = state->pindexLastCommonBlock; // Never fetch further than the best block we know the peer has, or more // than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last linked block we have in // common with this peer. The +1 is so we can detect stalling, namely if we // would be able to download that next block if the window were 1 larger. int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); NodeId waitingfor = -1; while (pindexWalk->nHeight < nMaxHeight) { // Read up to 128 (or more, if more blocks than that are needed) // successors of pindexWalk (towards pindexBestKnownBlock) into // vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as // expensive as iterating over ~100 CBlockIndex* entries anyway. int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); vToFetch.resize(nToFetch); pindexWalk = state->pindexBestKnownBlock->GetAncestor( pindexWalk->nHeight + nToFetch); vToFetch[nToFetch - 1] = pindexWalk; for (unsigned int i = nToFetch - 1; i > 0; i--) { vToFetch[i - 1] = vToFetch[i]->pprev; } // Iterate over those blocks in vToFetch (in forward direction), adding // the ones that are not yet downloaded and not in flight to vBlocks. In // the mean time, update pindexLastCommonBlock as long as all ancestors // are already downloaded, or if it's already part of our chain (and // therefore don't need it even if pruned). for (const CBlockIndex *pindex : vToFetch) { if (!pindex->IsValid(BlockValidity::TREE)) { // We consider the chain that this peer is on invalid. return; } if (pindex->nStatus.hasData() || chainActive.Contains(pindex)) { if (pindex->nChainTx) { state->pindexLastCommonBlock = pindex; } } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { // The block is not already downloaded, and not yet in flight. if (pindex->nHeight > nWindowEnd) { // We reached the end of the window. if (vBlocks.size() == 0 && waitingfor != nodeid) { // We aren't able to fetch anything, but we would be if // the download window was one larger. nodeStaller = waitingfor; } return; } vBlocks.push_back(pindex); if (vBlocks.size() == count) { return; } } else if (waitingfor == -1) { // This is the first already-in-flight block. waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } } } } } // namespace // This function is used for testing the stale tip eviction logic, see // denialofservice_tests.cpp void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { LOCK(cs_main); CNodeState *state = State(node); if (state) { state->m_last_block_announcement = time_in_seconds; } } // Returns true for outbound peers, excluding manual connections, feelers, and // one-shots. static bool IsOutboundDisconnectionCandidate(const CNode *node) { return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot); } void PeerLogicValidation::InitializeNode(const Config &config, CNode *pnode) { CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); { LOCK(cs_main); mapNodeState.emplace_hint( mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); } if (!pnode->fInbound) { PushNodeVersion(config, pnode, connman, GetTime()); } } void PeerLogicValidation::FinalizeNode(const Config &config, NodeId nodeid, bool &fUpdateConnectionTime) { fUpdateConnectionTime = false; LOCK(cs_main); CNodeState *state = State(nodeid); assert(state != nullptr); if (state->fSyncStarted) { nSyncStarted--; } if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { fUpdateConnectionTime = true; } for (const QueuedBlock &entry : state->vBlocksInFlight) { mapBlocksInFlight.erase(entry.hash); } EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); assert(nPeersWithValidatedDownloads >= 0); g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect; assert(g_outbound_peers_with_protect_from_disconnect >= 0); mapNodeState.erase(nodeid); if (mapNodeState.empty()) { // Do a consistency check after the last peer is removed. assert(mapBlocksInFlight.empty()); assert(nPreferredDownload == 0); assert(nPeersWithValidatedDownloads == 0); assert(g_outbound_peers_with_protect_from_disconnect == 0); } LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); CNodeState *state = State(nodeid); if (state == nullptr) { return false; } stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; for (const QueuedBlock &queue : state->vBlocksInFlight) { if (queue.pindex) { stats.vHeightInFlight.push_back(queue.pindex->nHeight); } } return true; } ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions // static void AddToCompactExtraTransactions(const CTransactionRef &tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); if (max_extra_txn <= 0) { return; } if (!vExtraTxnForCompact.size()) { vExtraTxnForCompact.resize(max_extra_txn); } vExtraTxnForCompact[vExtraTxnForCompactIt] = std::make_pair(tx->GetId(), tx); vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; } bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { const uint256 &txid = tx->GetId(); if (mapOrphanTransactions.count(txid)) { return false; } // Ignore big transactions, to avoid a send-big-orphans memory exhaustion // attack. If a peer has a legitimate large transaction with a missing // parent then we assume it will rebroadcast it later, after the parent // transaction(s) have been mined or received. // 100 orphans, each of which is at most 99,999 bytes big is at most 10 // megabytes of orphans and somewhat more byprev index (in the worst case): unsigned int sz = tx->GetTotalSize(); if (sz >= MAX_STANDARD_TX_SIZE) { LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, txid.ToString()); return false; } auto ret = mapOrphanTransactions.emplace( txid, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); assert(ret.second); for (const CTxIn &txin : tx->vin) { mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); } AddToCompactExtraTransactions(tx); LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", txid.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; } static int EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { std::map::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) { return 0; } for (const CTxIn &txin : it->second.tx->vin) { auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); if (itPrev == mapOrphanTransactionsByPrev.end()) { continue; } itPrev->second.erase(it); if (itPrev->second.empty()) { mapOrphanTransactionsByPrev.erase(itPrev); } } mapOrphanTransactions.erase(it); return 1; } void EraseOrphansFor(NodeId peer) { LOCK(g_cs_orphans); int nErased = 0; std::map::iterator iter = mapOrphanTransactions.begin(); while (iter != mapOrphanTransactions.end()) { // Increment to avoid iterator becoming invalid. std::map::iterator maybeErase = iter++; if (maybeErase->second.fromPeer == peer) { nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); } } if (nErased > 0) { LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer); } } unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { LOCK(g_cs_orphans); unsigned int nEvicted = 0; static int64_t nNextSweep; int64_t nNow = GetTime(); if (nNextSweep <= nNow) { // Sweep out expired orphan pool entries: int nErased = 0; int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; std::map::iterator iter = mapOrphanTransactions.begin(); while (iter != mapOrphanTransactions.end()) { std::map::iterator maybeErase = iter++; if (maybeErase->second.nTimeExpire <= nNow) { nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); } else { nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime); } } // Sweep again 5 minutes after the next entry that expires in order to // batch the linear scan. nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; if (nErased > 0) { LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased); } } FastRandomContext rng; while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: uint256 randomhash = rng.rand256(); std::map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) { it = mapOrphanTransactions.begin(); } EraseOrphanTx(it->first); ++nEvicted; } return nEvicted; } /** * Mark a misbehaving peer to be banned depending upon the value of `-banscore`. */ void Misbehaving(NodeId pnode, int howmuch, const std::string &reason) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (howmuch == 0) { return; } CNodeState *state = State(pnode); if (state == nullptr) { return; } state->nMisbehavior += howmuch; int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf( "%s: %s peer=%d (%d -> %d) reason: %s BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior - howmuch, state->nMisbehavior, reason.c_str()); state->fShouldBan = true; } else { LogPrintf("%s: %s peer=%d (%d -> %d) reason: %s\n", __func__, state->name, pnode, state->nMisbehavior - howmuch, state->nMisbehavior, reason.c_str()); } } // overloaded variant of above to operate on CNode*s static void Misbehaving(CNode *node, int howmuch, const std::string &reason) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { Misbehaving(node->GetId(), howmuch, reason); } ////////////////////////////////////////////////////////////////////////////// // // blockchain -> download logic notification // // To prevent fingerprinting attacks, only send blocks/headers outside of the // active chain if they are no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about and // we fully-validated them at some point. static bool BlockRequestAllowed(const CBlockIndex *pindex, const Consensus::Params &consensusParams) { AssertLockHeld(cs_main); if (chainActive.Contains(pindex)) { return true; } return pindex->IsValid(BlockValidity::SCRIPTS) && (pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); } PeerLogicValidation::PeerLogicValidation(CConnman *connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); const Consensus::Params &consensusParams = Params().GetConsensus(); // Stale tip checking and peer eviction are on two different timers, but we // don't want them to get out of sync due to drift in the scheduler, so we // combine them in one function and schedule at the quicker (peer-eviction) // timer. static_assert( EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer"); scheduler.scheduleEvery( [this, &consensusParams]() { this->CheckForStaleTipAndEvictPeers(consensusParams); return true; }, EXTRA_PEER_CHECK_INTERVAL * 1000); } void PeerLogicValidation::BlockConnected( const std::shared_ptr &pblock, const CBlockIndex *pindex, const std::vector &vtxConflicted) { LOCK(g_cs_orphans); std::vector vOrphanErase; for (const CTransactionRef &ptx : pblock->vtx) { const CTransaction &tx = *ptx; // Which orphan pool entries must we evict? for (const auto &txin : tx.vin) { auto itByPrev = mapOrphanTransactionsByPrev.find(txin.prevout); if (itByPrev == mapOrphanTransactionsByPrev.end()) { continue; } for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) { const CTransaction &orphanTx = *(*mi)->second.tx; const uint256 &orphanHash = orphanTx.GetHash(); vOrphanErase.push_back(orphanHash); } } } // Erase orphan transactions include or precluded by this block if (vOrphanErase.size()) { int nErased = 0; for (uint256 &orphanId : vOrphanErase) { nErased += EraseOrphanTx(orphanId); } LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased); } g_last_tip_update = GetTime(); } // All of the following cache a recent block, and are protected by // cs_most_recent_block static CCriticalSection cs_most_recent_block; static std::shared_ptr most_recent_block GUARDED_BY(cs_most_recent_block); static std::shared_ptr most_recent_compact_block GUARDED_BY(cs_most_recent_block); static uint256 most_recent_block_hash GUARDED_BY(cs_most_recent_block); void PeerLogicValidation::NewPoWValidBlock( const CBlockIndex *pindex, const std::shared_ptr &pblock) { std::shared_ptr pcmpctblock = std::make_shared(*pblock); const CNetMsgMaker msgMaker(PROTOCOL_VERSION); LOCK(cs_main); static int nHighestFastAnnounce = 0; if (pindex->nHeight <= nHighestFastAnnounce) { return; } nHighestFastAnnounce = pindex->nHeight; uint256 hashBlock(pblock->GetHash()); { LOCK(cs_most_recent_block); most_recent_block_hash = hashBlock; most_recent_block = pblock; most_recent_compact_block = pcmpctblock; } connman->ForEachNode([this, &pcmpctblock, pindex, &msgMaker, &hashBlock](CNode *pnode) { AssertLockHeld(cs_main); // TODO: Avoid the repeated-serialization here if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) { return; } ProcessBlockAvailability(pnode->GetId()); CNodeState &state = *State(pnode->GetId()); // If the peer has, or we announced to them the previous block already, // but we don't think they have this one, go ahead and announce it. if (state.fPreferHeaderAndIDs && !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) { LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock", hashBlock.ToString(), pnode->GetId()); connman->PushMessage( pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); state.pindexBestHeaderSent = pindex; } }); } void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; connman->SetBestHeight(nNewHeight); SetServiceFlagsIBDCache(!fInitialDownload); if (!fInitialDownload) { // Find the hashes of all blocks that weren't previously in the best // chain. std::vector vHashes; const CBlockIndex *pindexToAnnounce = pindexNew; while (pindexToAnnounce != pindexFork) { vHashes.push_back(pindexToAnnounce->GetBlockHash()); pindexToAnnounce = pindexToAnnounce->pprev; if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { // Limit announcements in case of a huge reorganization. Rely on // the peer's synchronization mechanism in that case. break; } } // Relay inventory, but don't relay old inventory during initial block // download. connman->ForEachNode([nNewHeight, &vHashes](CNode *pnode) { if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 0)) { for (const uint256 &hash : reverse_iterate(vHashes)) { pnode->PushBlockHash(hash); } } }); connman->WakeMessageHandler(); } nTimeBestReceived = GetTime(); } void PeerLogicValidation::BlockChecked(const CBlock &block, const CValidationState &state) { LOCK(cs_main); const uint256 hash(block.GetHash()); std::map>::iterator it = mapBlockSource.find(hash); int nDoS = 0; if (state.IsInvalid(nDoS)) { // Don't send reject message with code 0 or an internal reject code. if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { CBlockReject reject = { uint8_t(state.GetRejectCode()), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash}; State(it->second.first)->rejects.push_back(reject); if (nDoS > 0 && it->second.second) { Misbehaving(it->second.first, nDoS, state.GetRejectReason()); } } } // Check that: // 1. The block is valid // 2. We're not in initial block download // 3. This is currently the best block we're aware of. We haven't updated // the tip yet so we have no way to check this directly here. Instead we // just check that there are currently no other blocks in flight. else if (state.IsValid() && !IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman); } } if (it != mapBlockSource.end()) { mapBlockSource.erase(it); } } ////////////////////////////////////////////////////////////////////////////// // // Messages // static bool AlreadyHave(const CInv &inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { case MSG_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) { // If the chain tip has changed previously rejected transactions // might be now valid, e.g. due to a nLockTime'd tx becoming // valid, or a double-spend. Reset the rejects filter and give // those txs a second chance. hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); recentRejects->reset(); } { LOCK(g_cs_orphans); if (mapOrphanTransactions.count(inv.hash)) { return true; } } // Use pcoinsTip->HaveCoinInCache as a quick approximation to // exclude requesting or processing some txs which have already been // included in a block. As this is best effort, we only check for // output 0 and 1. This works well enough in practice and we get // diminishing returns with 2 onward. const TxId txid(inv.hash); return recentRejects->contains(inv.hash) || g_mempool.exists(inv.hash) || pcoinsTip->HaveCoinInCache(COutPoint(txid, 0)) || pcoinsTip->HaveCoinInCache(COutPoint(txid, 1)); } case MSG_BLOCK: return LookupBlockIndex(inv.hash) != nullptr; } // Don't know what it is, just say we already got one return true; } static void RelayTransaction(const CTransaction &tx, CConnman *connman) { CInv inv(MSG_TX, tx.GetId()); connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); }); } static void RelayAddress(const CAddress &addr, bool fReachable, CConnman *connman) { // Limited relaying of addresses outside our network(s) unsigned int nRelayNodes = fReachable ? 2 : 1; // Relay to a limited number of other nodes. // Use deterministic randomness to send to the same nodes for 24 hours at a // time so the addrKnowns of the chosen nodes prevent repeats. uint64_t hashAddr = addr.GetHash(); const CSipHasher hasher = connman->GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY) .Write(hashAddr << 32) .Write((GetTime() + hashAddr) / (24 * 60 * 60)); FastRandomContext insecure_rand; std::array, 2> best{ {{0, nullptr}, {0, nullptr}}}; assert(nRelayNodes <= best.size()); auto sortfunc = [&best, &hasher, nRelayNodes](CNode *pnode) { if (pnode->nVersion >= CADDR_TIME_VERSION) { uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize(); for (unsigned int i = 0; i < nRelayNodes; i++) { if (hashKey > best[i].first) { std::copy(best.begin() + i, best.begin() + nRelayNodes - 1, best.begin() + i + 1); best[i] = std::make_pair(hashKey, pnode); break; } } } }; auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] { for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) { best[i].second->PushAddress(addr, insecure_rand); } }; connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } void static ProcessGetBlockData(const Config &config, CNode *pfrom, const CInv &inv, CConnman *connman, const std::atomic &interruptMsgProc) { const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); bool send = false; std::shared_ptr a_recent_block; std::shared_ptr a_recent_compact_block; { LOCK(cs_most_recent_block); a_recent_block = most_recent_block; a_recent_compact_block = most_recent_compact_block; } bool need_activate_chain = false; { LOCK(cs_main); const CBlockIndex *pindex = LookupBlockIndex(inv.hash); if (pindex) { if (pindex->nChainTx && !pindex->IsValid(BlockValidity::SCRIPTS) && pindex->IsValid(BlockValidity::TREE)) { // If we have the block and all of its parents, but have not yet // validated it, we might be in the middle of connecting it (ie // in the unlock of cs_main before ActivateBestChain but after // AcceptBlock). In this case, we need to run ActivateBestChain // prior to checking the relay conditions below. need_activate_chain = true; } } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { CValidationState dummy; ActivateBestChain(config, dummy, a_recent_block); } LOCK(cs_main); const CBlockIndex *pindex = LookupBlockIndex(inv.hash); if (pindex) { send = BlockRequestAllowed(pindex, consensusParams); if (!send) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old " "block that isn't in the main chain\n", __func__, pfrom->GetId()); } } const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); // Disconnect node in case we have reached the outbound limit for serving // historical blocks. // Never disconnect whitelisted nodes. if (send && connman->OutboundTargetReached(true) && (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, " "disconnect peer=%d\n", pfrom->GetId()); // disconnect node pfrom->fDisconnect = true; send = false; } // Avoid leaking prune-height by never sending blocks below the // NODE_NETWORK_LIMITED threshold. // Add two blocks buffer extension for possible races if (send && !pfrom->fWhitelisted && ((((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2)))) { LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED " "threshold from peer=%d\n", pfrom->GetId()); // disconnect node and prevent it from stalling (would otherwise wait // for the missing block) pfrom->fDisconnect = true; send = false; } // Pruned nodes may have deleted the block, so check whether it's available // before trying to send. if (send && pindex->nStatus.hasData()) { std::shared_ptr pblock; if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { pblock = a_recent_block; } else { // Send block from disk std::shared_ptr pblockRead = std::make_shared(); if (!ReadBlockFromDisk(*pblockRead, pindex, config)) { assert(!"cannot load block from disk"); } pblock = pblockRead; } if (inv.type == MSG_BLOCK) { connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); } else if (inv.type == MSG_FILTERED_BLOCK) { bool sendMerkleBlock = false; CMerkleBlock merkleBlock; { LOCK(pfrom->cs_filter); if (pfrom->pfilter) { sendMerkleBlock = true; merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter); } } if (sendMerkleBlock) { connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); // CMerkleBlock just contains hashes, so also push any // transactions in the block the client did not see. This avoids // hurting performance by pointlessly requiring a round-trip. // Note that there is currently no way for a node to request any // single transactions we didn't send here - they must either // disconnect and retry or request the full block. Thus, the // protocol spec specified allows for us to provide duplicate // txn here, however we MUST always provide at least what the // remote peer needs. typedef std::pair PairType; for (PairType &pair : merkleBlock.vMatchedTxn) { connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::TX, *pblock->vtx[pair.first])); } } // else // no response } else if (inv.type == MSG_CMPCT_BLOCK) { // If a peer is asking for old blocks, we're almost guaranteed they // won't have a useful mempool to match against a compact block, and // we don't feel like constructing the object for them, so instead // we respond with the full, non-compact block. int nSendFlags = 0; if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { CBlockHeaderAndShortTxIDs cmpctblock(*pblock); connman->PushMessage( pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } else { connman->PushMessage( pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } } // Trigger the peer node to send a getblocks request for the next batch // of inventory. if (inv.hash == pfrom->hashContinue) { // Bypass PushInventory, this must send even if redundant, and we // want it right after the last block so they don't wait for other // stuff first. std::vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); pfrom->hashContinue.SetNull(); } } } static void ProcessGetData(const Config &config, CNode *pfrom, CConnman *connman, const std::atomic &interruptMsgProc) { AssertLockNotHeld(cs_main); std::deque::iterator it = pfrom->vRecvGetData.begin(); std::vector vNotFound; const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); { LOCK(cs_main); while (it != pfrom->vRecvGetData.end() && it->type == MSG_TX) { if (interruptMsgProc) { return; } // Don't bother if send buffer is too full to respond anyway. if (pfrom->fPauseSend) { break; } const CInv &inv = *it; it++; // Send stream from relay memory bool push = false; auto mi = mapRelay.find(inv.hash); int nSendFlags = 0; if (mi != mapRelay.end()) { connman->PushMessage( pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); push = true; } else if (pfrom->timeLastMempoolReq) { auto txinfo = g_mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool // when that TX couldn't have been INVed in reply to a MEMPOOL // request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { connman->PushMessage( pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); push = true; } } if (!push) { vNotFound.push_back(inv); } } } // release cs_main if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { const CInv &inv = *it; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) { it++; ProcessGetBlockData(config, pfrom, inv, connman, interruptMsgProc); } } pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it // doesn't have to wait around forever. Currently only SPV clients // actually care about this message: it's needed when they are // recursively walking the dependencies of relevant unconfirmed // transactions. SPV clients want to do that because they want to know // about (and store and rebroadcast and risk analyze) the dependencies // of transactions relevant to them, without having to download the // entire memory pool. connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); } } inline static void SendBlockTransactions(const CBlock &block, const BlockTransactionsRequest &req, CNode *pfrom, CConnman *connman) { BlockTransactions resp(req); for (size_t i = 0; i < req.indices.size(); i++) { if (req.indices[i] >= block.vtx.size()) { LOCK(cs_main); Misbehaving(pfrom, 100, "out-of-bound-tx-index"); LogPrintf( "Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId()); return; } resp.txn[i] = block.vtx[req.indices[i]]; } LOCK(cs_main); const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); int nSendFlags = 0; connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } static bool ProcessHeadersMessage(const Config &config, CNode *pfrom, CConnman *connman, const std::vector &headers, bool punish_duplicate_invalid) { const CChainParams &chainparams = config.GetChainParams(); const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); size_t nCount = headers.size(); if (nCount == 0) { // Nothing interesting. Stop asking this peers for more headers. return true; } bool received_new_header = false; const CBlockIndex *pindexLast = nullptr; { LOCK(cs_main); CNodeState *nodestate = State(pfrom->GetId()); // If this looks like it could be a block announcement (nCount < // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that // don't connect: // - Send a getheaders message in response to try to connect the chain. // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); LogPrint( BCLog::NET, "received header %s: missing prev block %s, sending getheaders " "(%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), pindexBestHeader->nHeight, pfrom->GetId(), nodestate->nUnconnectingHeaders); // Set hashLastUnknownBlock for this peer, so that if we eventually // get the headers - even from a different peer - we can use this // peer to download. UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { // The peer is sending us many headers we can't connect. Misbehaving(pfrom, 20, "too-many-unconnected-headers"); } return true; } uint256 hashLastBlock; for (const CBlockHeader &header : headers) { if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { Misbehaving(pfrom, 20, "disconnected-header"); return error("non-continuous headers sequence"); } hashLastBlock = header.GetHash(); } // If we don't have the last header, then they'll have given us // something new (if these headers are valid). if (!LookupBlockIndex(hashLastBlock)) { received_new_header = true; } } CValidationState state; CBlockHeader first_invalid_header; if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, &first_invalid_header)) { int nDoS; if (state.IsInvalid(nDoS)) { LOCK(cs_main); if (nDoS > 0) { Misbehaving(pfrom, nDoS, state.GetRejectReason()); } if (punish_duplicate_invalid && LookupBlockIndex(first_invalid_header.GetHash())) { // Goal: don't allow outbound peers to use up our outbound // connection slots if they are on incompatible chains. // // We ask the caller to set punish_invalid appropriately based // on the peer and the method of header delivery (compact blocks // are allowed to be invalid in some circumstances, under BIP // 152). // Here, we try to detect the narrow situation that we have a // valid block header (ie it was valid at the time the header // was received, and hence stored in mapBlockIndex) but know the // block is invalid, and that a peer has announced that same // block as being on its active chain. Disconnect the peer in // such a situation. // // Note: if the header that is invalid was not accepted to our // mapBlockIndex at all, that may also be grounds for // disconnecting the peer, as the chain they are on is likely to // be incompatible. However, there is a circumstance where that // does not hold: if the header's timestamp is more than 2 hours // ahead of our current time. In that case, the header may // become valid in the future, and we don't want to disconnect a // peer merely for serving us one too-far-ahead block header, to // prevent an attacker from splitting the network by mining a // block right at the 2 hour boundary. // // TODO: update the DoS logic (or, rather, rewrite the // DoS-interface between validation and net_processing) so that // the interface is cleaner, and so that we disconnect on all // the reasons that a peer's headers chain is incompatible with // ours (eg block->nVersion softforks, MTP violations, etc), and // not just the duplicate-invalid case. pfrom->fDisconnect = true; } return error("invalid header received"); } } { LOCK(cs_main); CNodeState *nodestate = State(pfrom->GetId()); if (nodestate->nUnconnectingHeaders > 0) { LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders); } nodestate->nUnconnectingHeaders = 0; assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); // From here, pindexBestKnownBlock should be guaranteed to be non-null, // because it is set in UpdateBlockAvailability. Some nullptr checks are // still present, however, as belt-and-suspenders. if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) { nodestate->m_last_block_announcement = GetTime(); } if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more // headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip // or pindexBestHeader, continue from there instead. LogPrint( BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. if (fCanDirectFetch && pindexLast->IsValid(BlockValidity::TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { std::vector vToFetch; const CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to // a limit. while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!pindexWalk->nStatus.hasData() && !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } pindexWalk = pindexWalk->pprev; } // If pindexWalk still isn't on our main chain, we're looking at a // very large reorg at a time we think we're close to caught up to // the main chain -- this shouldn't really happen. Bail out on the // direct fetch and rely on parallel download instead. if (!chainActive.Contains(pindexWalk)) { LogPrint( BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } else { std::vector vGetData; // Download as much as possible, from earliest to latest. for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // Can't download any more from this peer break; } vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(config, pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom->GetId()); } if (vGetData.size() > 1) { LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers " "direct fetch\n", pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BlockValidity::CHAIN)) { // In any case, we want to download using a compact // block, not a regular one. vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } } } // If we're in IBD, we want outbound peers that will serve us a useful // chain. Disconnect peers that are on chains with insufficient work. if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { // When nCount < MAX_HEADERS_RESULTS, we know we have no more // headers to fetch from this peer. if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { // This peer has too little work on their headers chain to help // us sync -- disconnect if using an outbound slot (unless // whitelisted or addnode). // Note: We compare their tip to nMinimumChainWork (rather than // chainActive.Tip()) because we won't start block download // until we have a headers chain that has at least // nMinimumChainWork, even if a peer has a chain past our tip, // as an anti-DoS measure. if (IsOutboundDisconnectionCandidate(pfrom)) { LogPrintf("Disconnecting outbound peer %d -- headers " "chain has insufficient work\n", pfrom->GetId()); pfrom->fDisconnect = true; } } } if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) { // If this is an outbound peer, check to see if we should protect it // from the bad/lagging chain logic. if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId()); nodestate->m_chain_sync.m_protect = true; ++g_outbound_peers_with_protect_from_disconnect; } } } return true; } static bool ProcessMessage(const Config &config, CNode *pfrom, const std::string &strCommand, CDataStream &vRecv, int64_t nTimeReceived, CConnman *connman, const std::atomic &interruptMsgProc) { const CChainParams &chainparams = config.GetChainParams(); LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } if (!(pfrom->GetLocalServices() & NODE_BLOOM) && (strCommand == NetMsgType::FILTERLOAD || strCommand == NetMsgType::FILTERADD)) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { LOCK(cs_main); Misbehaving(pfrom, 100, "no-bloom-version"); return false; } else { pfrom->fDisconnect = true; return false; } } if (strCommand == NetMsgType::REJECT) { if (LogAcceptCategory(BCLog::NET)) { try { std::string strMsg; uint8_t ccode; std::string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); std::ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) { uint256 hash; vRecv >> hash; ss << ": hash " << hash.ToString(); } LogPrint(BCLog::NET, "Reject %s\n", SanitizeString(ss.str())); } catch (const std::ios_base::failure &) { // Avoid feedback loops by preventing reject messages from // triggering a new reject message. LogPrint(BCLog::NET, "Unparseable reject message received\n"); } } } else if (strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) { connman->PushMessage( pfrom, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); LOCK(cs_main); Misbehaving(pfrom, 1, "multiple-version"); return false; } int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; uint64_t nServiceInt; ServiceFlags nServices; int nVersion; int nSendVersion; std::string strSubVer; std::string cleanSubVer; int nStartingHeight = -1; bool fRelay = true; vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; nSendVersion = std::min(nVersion, PROTOCOL_VERSION); nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { connman->SetServices(pfrom->addr, nServices); } if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices)) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services " "(%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); connman->PushMessage( pfrom, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); pfrom->fDisconnect = true; return false; } if (nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); connman->PushMessage( pfrom, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); pfrom->fDisconnect = true; return false; } if (!vRecv.empty()) { vRecv >> addrFrom >> nNonce; } if (!vRecv.empty()) { vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); cleanSubVer = SanitizeString(strSubVer); } if (!vRecv.empty()) { vRecv >> nStartingHeight; } if (!vRecv.empty()) { vRecv >> fRelay; } // Disconnect if we connected to ourself if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; return true; } if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); } // Be shy and don't send version until we hear if (pfrom->fInbound) { PushNodeVersion(config, pfrom, connman, GetAdjustedTime()); } connman->PushMessage( pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); pfrom->nServices = nServices; pfrom->SetAddrLocal(addrMe); { LOCK(pfrom->cs_SubVer); pfrom->strSubVer = strSubVer; pfrom->cleanSubVer = cleanSubVer; } pfrom->nStartingHeight = nStartingHeight; // set nodes not relaying blocks and tx and not serving (parts) of the // historical blockchain as "clients" pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED)); // set nodes not capable of serving the complete blockchain history as // "limited nodes" pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); { LOCK(pfrom->cs_filter); // set to true after we get the first filter* message pfrom->fRelayTxes = fRelay; } // Change version pfrom->SetSendVersion(nSendVersion); pfrom->nVersion = nVersion; // Potentially mark this peer as a preferred download peer. { LOCK(cs_main); UpdatePreferredDownload(pfrom, State(pfrom->GetId())); } if (!pfrom->fInbound) { // Advertise our address if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); FastRandomContext insecure_rand; if (addr.IsRoutable()) { LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(addrMe); LogPrint(BCLog::NET, "ProcessMessages: advertising address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } } // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000) { connman->PushMessage( pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom->fGetAddr = true; } connman->MarkAddressGood(pfrom->addr); } std::string remoteAddr; if (fLogIPs) { remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); } LogPrint(BCLog::NET, "receive version message: [%s] %s: version %d, blocks=%d, " "us=%s, peer=%d%s\n", pfrom->addr.ToString().c_str(), cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(), remoteAddr); int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); // If the peer is old enough to have the old alert system, send it the // final alert. if (pfrom->nVersion <= 70012) { CDataStream finalAlert( ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffef" "fff7f01ffffff7f00000000ffffff7f00ffffff7f002f55524745" "4e543a20416c657274206b657920636f6d70726f6d697365642c2" "075706772616465207265717569726564004630440220653febd6" "410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3ab" "d5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fec" "aae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); connman->PushMessage( pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); } // Feeler connections exist only to verify if address is online. if (pfrom->fFeeler) { assert(pfrom->fInbound == false); pfrom->fDisconnect = true; } return true; } else if (pfrom->nVersion == 0) { // Must have a version message before anything else LOCK(cs_main); Misbehaving(pfrom, 1, "missing-version"); return false; } // At this point, the outgoing message serialization version can't change. const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion( std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Mark this node as currently connected, so we update its timestamp // later. LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; LogPrintf( "New outbound peer connected: version: %d, blocks=%d, " "peer=%d%s\n", pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : "")); } if (pfrom->nVersion >= SENDHEADERS_VERSION) { // Tell our peer we prefer to receive headers rather than inv's // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 // cmpctblocks. However, we do not request new block announcements // using cmpctblock messages. We send this to non-NODE NETWORK peers // as well, because they may wish to request compact blocks from us. bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 1; connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } pfrom->fSuccessfullyConnected = true; } else if (!pfrom->fSuccessfullyConnected) { // Must have a verack message before anything else LOCK(cs_main); Misbehaving(pfrom, 1, "missing-verack"); return false; } else if (strCommand == NetMsgType::ADDR) { std::vector vAddr; vRecv >> vAddr; // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000) { return true; } if (vAddr.size() > 1000) { LOCK(cs_main); Misbehaving(pfrom, 20, "oversized-addr"); return error("message addr size() = %u", vAddr.size()); } // Store the new addresses std::vector vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; for (CAddress &addr : vAddr) { if (interruptMsgProc) { return true; } // We only bother storing full nodes, though this may include things // which we would not make an outbound connection to, in part // because we may make feeler connections to them. if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices)) { continue; } if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) { addr.nTime = nNow - 5 * 24 * 60 * 60; } pfrom->AddAddressKnown(addr); bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes RelayAddress(addr, fReachable, connman); } // Do not store addresses outside our network if (fReachable) { vAddrOk.push_back(addr); } } connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) { pfrom->fGetAddr = false; } if (pfrom->fOneShot) { pfrom->fDisconnect = true; } } else if (strCommand == NetMsgType::SENDHEADERS) { LOCK(cs_main); State(pfrom->GetId())->fPreferHeaders = true; } else if (strCommand == NetMsgType::SENDCMPCT) { bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 0; vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; if (nCMPCTBLOCKVersion == 1) { LOCK(cs_main); // fProvidesHeaderAndIDs is used to "lock in" version of compact // blocks we send. if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) { State(pfrom->GetId())->fProvidesHeaderAndIDs = true; } State(pfrom->GetId())->fPreferHeaderAndIDs = fAnnounceUsingCMPCTBLOCK; if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) { State(pfrom->GetId())->fSupportsDesiredCmpctVersion = true; } } } else if (strCommand == NetMsgType::INV) { std::vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { LOCK(cs_main); Misbehaving(pfrom, 20, "oversized-inv"); return error("message inv size() = %u", vInv.size()); } bool fBlocksOnly = !fRelayTxes; // Allow whitelisted peers to send data other than blocks in blocks only // mode if whitelistrelay is true if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) { fBlocksOnly = false; } LOCK(cs_main); for (auto &inv : vInv) { if (interruptMsgProc) { return true; } bool fAlreadyHave = AlreadyHave(inv); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->GetId()); if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { // We used to request the full block here, but since // headers-announcements are now the primary method of // announcement on the network, and since, in the case that // a node fell back to inv we probably have a reorg which we // should get the headers for first, we now only provide a // getheaders response here. When we receive the headers, we // will then ask for the blocks we need. connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId()); } } else { pfrom->AddInventoryKnown(inv); if (fBlocksOnly) { LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of " "protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId()); } else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) { pfrom->AskFor(inv); } } } } else if (strCommand == NetMsgType::GETDATA) { std::vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { LOCK(cs_main); Misbehaving(pfrom, 20, "too-many-inv"); return error("message getdata size() = %u", vInv.size()); } LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->GetId()); if (vInv.size() > 0) { LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->GetId()); } pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(config, pfrom, connman, interruptMsgProc); } else if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; // We might have announced the currently-being-connected tip using a // compact block, which resulted in the peer sending a getblocks // request, which we would otherwise respond to without the new block. // To avoid this situation we simply verify that we are on our best // known chain now. This is super overkill, but we handle it better // for getheaders requests, and there are no known nodes which support // compact blocks but still use getblocks to request blocks. { std::shared_ptr a_recent_block; { LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } CValidationState dummy; ActivateBestChain(config, dummy, a_recent_block); } LOCK(cs_main); // Find the last block the caller has in the main chain const CBlockIndex *pindex = FindForkInGlobalIndex(chainActive, locator); // Send the rest of the chain if (pindex) { pindex = chainActive.Next(pindex); } int nLimit = 500; LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->GetId()); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } // If pruning, don't inv blocks unless we have on disk and are // likely to still have for some reasonable time window (1 hour) // that block relay might require. const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing; if (fPruneMode && (!pindex->nStatus.hasData() || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave)) { LogPrint( BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { // When this block is requested, we'll send an inv that'll // trigger the peer to getblocks the next batch of inventory. LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); pfrom->hashContinue = pindex->GetBlockHash(); break; } } } else if (strCommand == NetMsgType::GETBLOCKTXN) { BlockTransactionsRequest req; vRecv >> req; std::shared_ptr recent_block; { LOCK(cs_most_recent_block); if (most_recent_block_hash == req.blockhash) { recent_block = most_recent_block; } // Unlock cs_most_recent_block to avoid cs_main lock inversion } if (recent_block) { SendBlockTransactions(*recent_block, req, pfrom, connman); return true; } LOCK(cs_main); const CBlockIndex *pindex = LookupBlockIndex(req.blockhash); if (!pindex || !pindex->nStatus.hasData()) { LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId()); return true; } if (pindex->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) { // If an older block is requested (should never happen in practice, // but can happen in tests) send a block response instead of a // blocktxn response. Sending a full block response instead of a // small blocktxn response is preferable in the case where a peer // might maliciously send lots of getblocktxn requests to trigger // expensive disk reads, because it will require the peer to // actually receive all the data read from disk over the network. LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->GetId(), MAX_BLOCKTXN_DEPTH); CInv inv; inv.type = MSG_BLOCK; inv.hash = req.blockhash; pfrom->vRecvGetData.push_back(inv); // The message processing loop will go around again (without // pausing) and we'll respond then (without cs_main) return true; } CBlock block; bool ret = ReadBlockFromDisk(block, pindex, config); assert(ret); SendBlockTransactions(block, req, pfrom, connman); } else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; LOCK(cs_main); if (IsInitialBlockDownload() && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in " "initial block download\n", pfrom->GetId()); return true; } CNodeState *nodestate = State(pfrom->GetId()); const CBlockIndex *pindex = nullptr; if (locator.IsNull()) { // If locator is null, return the hashStop block pindex = LookupBlockIndex(hashStop); if (!pindex) { return true; } if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block " "header that isn't in the main chain\n", __func__, pfrom->GetId()); return true; } } else { // Find the last block the caller has in the main chain pindex = FindForkInGlobalIndex(chainActive, locator); if (pindex) { pindex = chainActive.Next(pindex); } } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx // count at the end std::vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom->GetId()); for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) { break; } } // pindex can be nullptr either if we sent chainActive.Tip() OR // if our peer has chainActive.Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. // // It is important that we simply reset the BestHeaderSent value here, // and not max(BestHeaderSent, newHeaderSent). We might have announced // the currently-being-connected tip using a compact block, which // resulted in the peer sending a headers request, which we respond to // without the new block. By resetting the BestHeaderSent, we ensure we // will re-announce the new block via headers (or compact blocks again) // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } else if (strCommand == NetMsgType::TX) { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or // whitelistrelay is off if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId()); return true; } std::deque vWorkQueue; std::vector vEraseQueue; CTransactionRef ptx; vRecv >> ptx; const CTransaction &tx = *ptx; CInv inv(MSG_TX, tx.GetId()); pfrom->AddInventoryKnown(inv); LOCK2(cs_main, g_cs_orphans); bool fMissingInputs = false; CValidationState state; const TxId txid(inv.hash); pfrom->setAskFor.erase(txid); mapAlreadyAskedFor.erase(txid); if (!AlreadyHave(inv) && AcceptToMemoryPool(config, g_mempool, state, ptx, true, &fMissingInputs)) { g_mempool.check(pcoinsTip.get()); RelayTransaction(tx, connman); for (size_t i = 0; i < tx.vout.size(); i++) { vWorkQueue.emplace_back(txid, i); } pfrom->nLastTXTime = GetTime(); LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s " "(poolsz %u txn, %u kB)\n", pfrom->GetId(), tx.GetId().ToString(), g_mempool.size(), g_mempool.DynamicMemoryUsage() / 1000); // Recursively process any orphan transactions that depended on this // one std::unordered_map rejectCountPerNode; while (!vWorkQueue.empty()) { auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front()); vWorkQueue.pop_front(); if (itByPrev == mapOrphanTransactionsByPrev.end()) { continue; } for (auto mi = itByPrev->second.begin(); mi != itByPrev->second.end(); ++mi) { const CTransactionRef &porphanTx = (*mi)->second.tx; const CTransaction &orphanTx = *porphanTx; const TxId &orphanId = orphanTx.GetId(); NodeId fromPeer = (*mi)->second.fromPeer; bool fMissingInputs2 = false; // Use a dummy CValidationState so someone can't setup nodes // to counter-DoS based on orphan resolution (that is, // feeding people an invalid transaction based on LegitTxX // in order to get anyone relaying LegitTxX banned) CValidationState stateDummy; auto it = rejectCountPerNode.find(fromPeer); if (it != rejectCountPerNode.end() && it->second > MAX_NON_STANDARD_ORPHAN_PER_NODE) { continue; } if (AcceptToMemoryPool(config, g_mempool, stateDummy, porphanTx, true, &fMissingInputs2)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanId.ToString()); RelayTransaction(orphanTx, connman); for (size_t i = 0; i < orphanTx.vout.size(); i++) { vWorkQueue.emplace_back(orphanId, i); } vEraseQueue.push_back(orphanId); } else if (!fMissingInputs2) { int nDos = 0; if (stateDummy.IsInvalid(nDos)) { rejectCountPerNode[fromPeer]++; if (nDos > 0) { // Punish peer that gave us an invalid orphan tx Misbehaving(fromPeer, nDos, "invalid-orphan-tx"); LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanId.ToString()); } } // Has inputs but not accepted to mempool // Probably non-standard or insufficient fee/priority LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanId.ToString()); vEraseQueue.push_back(orphanId); if (!stateDummy.CorruptionPossible()) { // Do not use rejection cache for witness // transactions or witness-stripped transactions, as // they can have been malleated. See // https://github.com/bitcoin/bitcoin/issues/8279 // for details. assert(recentRejects); recentRejects->insert(orphanId); } } g_mempool.check(pcoinsTip.get()); } } for (const uint256 &hash : vEraseQueue) { EraseOrphanTx(hash); } } else if (fMissingInputs) { // It may be the case that the orphans parents have all been // rejected. bool fRejectedParents = false; for (const CTxIn &txin : tx.vin) { if (recentRejects->contains(txin.prevout.GetTxId())) { fRejectedParents = true; break; } } if (!fRejectedParents) { for (const CTxIn &txin : tx.vin) { // FIXME: MSG_TX should use a TxHash, not a TxId. CInv _inv(MSG_TX, txin.prevout.GetTxId()); pfrom->AddInventoryKnown(_inv); if (!AlreadyHave(_inv)) { pfrom->AskFor(_inv); } } AddOrphanTx(ptx, pfrom->GetId()); // DoS prevention: do not allow mapOrphanTransactions to grow // unbounded unsigned int nMaxOrphanTx = (unsigned int)std::max( int64_t(0), gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) { LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted); } } else { LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n", tx.GetId().ToString()); // We will continue to reject this tx since it has rejected // parents so avoid re-requesting it from other peers. recentRejects->insert(tx.GetId()); } } else { if (!state.CorruptionPossible()) { // Do not use rejection cache for witness transactions or // witness-stripped transactions, as they can have been // malleated. See https://github.com/bitcoin/bitcoin/issues/8279 // for details. assert(recentRejects); recentRejects->insert(tx.GetId()); if (RecursiveDynamicUsage(*ptx) < 100000) { AddToCompactExtraTransactions(ptx); } } if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, // even if they were already in the mempool or rejected from it // due to policy, allowing the node to function as a gateway for // nodes hidden behind it. // // Never relay transactions that we would assign a non-zero DoS // score for, as we expect peers to do the same with us in that // case. int nDoS = 0; if (!state.IsInvalid(nDoS) || nDoS == 0) { LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetId().ToString(), pfrom->GetId()); RelayTransaction(tx, connman); } else { LogPrintf("Not relaying invalid transaction %s from " "whitelisted peer=%d (%s)\n", tx.GetId().ToString(), pfrom->GetId(), FormatStateMessage(state)); } } } int nDoS = 0; if (state.IsInvalid(nDoS)) { LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state)); // Never send AcceptToMemoryPool's internal codes over P2P. if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, uint8_t(state.GetRejectCode()), state.GetRejectReason().substr( 0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); } if (nDoS > 0) { Misbehaving(pfrom, nDoS, state.GetRejectReason()); } } } // Ignore blocks received while importing else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { CBlockHeaderAndShortTxIDs cmpctblock; vRecv >> cmpctblock; bool received_new_header = false; { LOCK(cs_main); if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in // AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) { connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); } return true; } if (!LookupBlockIndex(cmpctblock.header.GetHash())) { received_new_header = true; } } const CBlockIndex *pindex = nullptr; CValidationState state; if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, &pindex)) { int nDoS; if (state.IsInvalid(nDoS)) { if (nDoS > 0) { LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()); LOCK(cs_main); Misbehaving(pfrom, nDoS, state.GetRejectReason()); } else { LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()); } return true; } } // When we succeed in decoding a block's txids from a cmpctblock // message we typically jump to the BLOCKTXN handling code, with a // dummy (empty) BLOCKTXN message, to re-use the logic there in // completing processing of the putative block (without cs_main). bool fProcessBLOCKTXN = false; CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION); // If we end up treating this as a plain headers message, call that as // well // without cs_main. bool fRevertToHeaderProcessing = false; // Keep a CBlock for "optimistic" compactblock reconstructions (see // below) std::shared_ptr pblock = std::make_shared(); bool fBlockReconstructed = false; { LOCK2(cs_main, g_cs_orphans); // If AcceptBlockHeader returned true, it set pindex assert(pindex); UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); CNodeState *nodestate = State(pfrom->GetId()); // If this was a new header with more work than our tip, update the // peer's last block announcement time if (received_new_header && pindex->nChainWork > chainActive.Tip()->nChainWork) { nodestate->m_last_block_announcement = GetTime(); } std::map::iterator>>:: iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash()); bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end(); if (pindex->nStatus.hasData()) { // Nothing to do here return true; } if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better pindex->nTx != 0) { // We had this block at some point, but pruned it if (fAlreadyInFlight) { // We requested this block for some reason, but our mempool // will probably be useless so we just grab the block via // normal getdata. std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); } return true; } // If we're not close to tip yet, give up and let parallel block // fetch work its magic. if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus())) { return true; } // We want to be a bit conservative just to be extra careful about // DoS possibilities in compact block processing... if (pindex->nHeight <= chainActive.Height() + 2) { if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) || (fAlreadyInFlight && blockInFlightIt->second.first == pfrom->GetId())) { std::list::iterator *queuedBlockIt = nullptr; if (!MarkBlockAsInFlight(config, pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex, &queuedBlockIt)) { if (!(*queuedBlockIt)->partialBlock) { (*queuedBlockIt) ->partialBlock.reset( new PartiallyDownloadedBlock(config, &g_mempool)); } else { // The block was already in flight using compact // blocks from the same peer. LogPrint(BCLog::NET, "Peer sent us compact block " "we were already syncing!\n"); return true; } } PartiallyDownloadedBlock &partialBlock = *(*queuedBlockIt)->partialBlock; ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status == READ_STATUS_INVALID) { // Reset in-flight state in case of whitelist MarkBlockAsReceived(pindex->GetBlockHash()); Misbehaving(pfrom, 100, "invalid-cmpctblk"); LogPrintf("Peer %d sent us invalid compact block\n", pfrom->GetId()); return true; } else if (status == READ_STATUS_FAILED) { // Duplicate txindices, the block is now in-flight, so // just request it. std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } BlockTransactionsRequest req; for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { if (!partialBlock.IsTxAvailable(i)) { req.indices.push_back(i); } } if (req.indices.empty()) { // Dirty hack to jump to BLOCKTXN code (TODO: move // message handling into their own functions) BlockTransactions txn; txn.blockhash = cmpctblock.header.GetHash(); blockTxnMsg << txn; fProcessBLOCKTXN = true; } else { req.blockhash = pindex->GetBlockHash(); connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); } } else { // This block is either already in flight from a different // peer, or this peer has too many blocks outstanding to // download from. Optimistically try to reconstruct anyway // since we might be able to without any round trips. PartiallyDownloadedBlock tempBlock(config, &g_mempool); ReadStatus status = tempBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status != READ_STATUS_OK) { // TODO: don't ignore failures return true; } std::vector dummy; status = tempBlock.FillBlock(*pblock, dummy); if (status == READ_STATUS_OK) { fBlockReconstructed = true; } } } else { if (fAlreadyInFlight) { // We requested this block, but its far into the future, so // our mempool will probably be useless - request the block // normally. std::vector vInv(1); vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } else { // If this was an announce-cmpctblock, we want the same // treatment as a header message. fRevertToHeaderProcessing = true; } } } // cs_main if (fProcessBLOCKTXN) { return ProcessMessage(config, pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, connman, interruptMsgProc); } if (fRevertToHeaderProcessing) { // Headers received from HB compact block peers are permitted to be // relayed before full validation (see BIP 152), so we don't want to // disconnect the peer if the header turns out to be for an invalid // block. // Note that if a peer tries to build on an invalid chain, that will // be detected and the peer will be banned. return ProcessHeadersMessage(config, pfrom, connman, {cmpctblock.header}, /*punish_duplicate_invalid=*/false); } if (fBlockReconstructed) { // If we got here, we were able to optimistically reconstruct a // block that is in flight from some other peer. { LOCK(cs_main); mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false)); } bool fNewBlock = false; // Setting fForceProcessing to true means that we bypass some of // our anti-DoS protections in AcceptBlock, which filters // unrequested blocks that might be trying to waste our resources // (eg disk space). Because we only try to reconstruct blocks when // we're close to caught up (via the CanDirectFetch() requirement // above, combined with the behavior of not requesting blocks until // we have a chain with at least nMinimumChainWork), and we ignore // compact blocks with less work than our tip, it is safe to treat // reconstructed compact blocks as having been requested. ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { LOCK(cs_main); mapBlockSource.erase(pblock->GetHash()); } // hold cs_main for CBlockIndex::IsValid() LOCK(cs_main); if (pindex->IsValid(BlockValidity::TRANSACTIONS)) { // Clear download state for this block, which is in process from // some other peer. We do this after calling. ProcessNewBlock so // that a malleated cmpctblock announcement can't be used to // interfere with block relay. MarkBlockAsReceived(pblock->GetHash()); } } } else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing { BlockTransactions resp; vRecv >> resp; std::shared_ptr pblock = std::make_shared(); bool fBlockRead = false; { LOCK(cs_main); std::map::iterator>>:: iterator it = mapBlocksInFlight.find(resp.blockhash); if (it == mapBlocksInFlight.end() || !it->second.second->partialBlock || it->second.first != pfrom->GetId()) { LogPrint(BCLog::NET, "Peer %d sent us block transactions for block " "we weren't expecting\n", pfrom->GetId()); return true; } PartiallyDownloadedBlock &partialBlock = *it->second.second->partialBlock; ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); if (status == READ_STATUS_INVALID) { // Reset in-flight state in case of whitelist. MarkBlockAsReceived(resp.blockhash); Misbehaving(pfrom, 100, "invalid-cmpctblk-txns"); LogPrintf("Peer %d sent us invalid compact block/non-matching " "block transactions\n", pfrom->GetId()); return true; } else if (status == READ_STATUS_FAILED) { // Might have collided, fall back to getdata now :( std::vector invs; invs.push_back(CInv(MSG_BLOCK, resp.blockhash)); connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { // Block is either okay, or possibly we received // READ_STATUS_CHECKBLOCK_FAILED. // Note that CheckBlock can only fail for one of a few reasons: // 1. bad-proof-of-work (impossible here, because we've already // accepted the header) // 2. merkleroot doesn't match the transactions given (already // caught in FillBlock with READ_STATUS_FAILED, so // impossible here) // 3. the block is otherwise invalid (eg invalid coinbase, // block is too big, too many legacy sigops, etc). // So if CheckBlock failed, #3 is the only possibility. // Under BIP 152, we don't DoS-ban unless proof of work is // invalid (we don't require all the stateless checks to have // been run). This is handled below, so just treat this as // though the block was successfully read, and rely on the // handling in ProcessNewBlock to ensure the block index is // updated, reject messages go out, etc. // it is now an empty pointer MarkBlockAsReceived(resp.blockhash); fBlockRead = true; // mapBlockSource is only used for sending reject messages and // DoS scores, so the race between here and cs_main in // ProcessNewBlock is fine. BIP 152 permits peers to relay // compact blocks after validating the header only; we should // not punish peers if the block turns out to be invalid. mapBlockSource.emplace(resp.blockhash, std::make_pair(pfrom->GetId(), false)); } } // Don't hold cs_main when we call into ProcessNewBlock if (fBlockRead) { bool fNewBlock = false; // Since we requested this block (it was in mapBlocksInFlight), // force it to be processed, even if it would not be a candidate for // new tip (missing previous block, chain not long enough, etc) // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent // disk-space attacks), but this should be safe due to the // protections in the compact block handler -- see related comment // in compact block optimistic reconstruction handling. ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { LOCK(cs_main); mapBlockSource.erase(pblock->GetHash()); } } } // Ignore headers received while importing else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) { std::vector headers; // Bypass the normal CBlock deserialization, as we don't want to risk // deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { LOCK(cs_main); Misbehaving(pfrom, 20, "too-many-headers"); return error("headers message size = %u", nCount); } headers.resize(nCount); for (unsigned int n = 0; n < nCount; n++) { vRecv >> headers[n]; // Ignore tx count; assume it is 0. ReadCompactSize(vRecv); } // Headers received via a HEADERS message should be valid, and reflect // the chain the peer is on. If we receive a known-invalid header, // disconnect the peer if it is using one of our outbound connection // slots. bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection; return ProcessHeadersMessage(config, pfrom, connman, headers, should_punish); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) { // Ignore blocks received while importing. std::shared_ptr pblock = std::make_shared(); vRecv >> *pblock; LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId()); // Process all blocks from whitelisted peers, even if not requested, // unless we're still syncing with the network. Such an unrequested // block may still be processed, subject to the conditions in // AcceptBlock(). bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); const uint256 hash(pblock->GetHash()); { LOCK(cs_main); // Also always process if we requested the block explicitly, as we // may need it even though it is not a candidate for a new best tip. forceProcessing |= MarkBlockAsReceived(hash); // mapBlockSource is only used for sending reject messages and DoS // scores, so the race between here and cs_main in ProcessNewBlock // is fine. mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true)); } bool fNewBlock = false; ProcessNewBlock(config, pblock, forceProcessing, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { LOCK(cs_main); mapBlockSource.erase(pblock->GetHash()); } } else if (strCommand == NetMsgType::GETADDR) { // This asymmetric behavior for inbound and outbound connections was // introduced to prevent a fingerprinting attack: an attacker can send // specific fake addresses to users' AddrMan and later request them by // sending getaddr messages. Making nodes which are behind NAT and can // only make outgoing connections ignore the getaddr message mitigates // the attack. if (!pfrom->fInbound) { LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId()); return true; } // Only send one GetAddr response per connection to reduce resource // waste and discourage addr stamping of INV announcements. if (pfrom->fSentAddr) { LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->GetId()); return true; } pfrom->fSentAddr = true; pfrom->vAddrToSend.clear(); std::vector vAddr = connman->GetAddresses(); FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) { pfrom->PushAddress(addr, insecure_rand); } } else if (strCommand == NetMsgType::MEMPOOL) { if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect " "peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; return true; } if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect " "peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; return true; } LOCK(pfrom->cs_inventory); pfrom->fSendMempool = true; } else if (strCommand == NetMsgType::PING) { if (pfrom->nVersion > BIP0031_VERSION) { uint64_t nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful // features: // // 1) A remote node can quickly check if the connection is // operational. // 2) Remote nodes can measure the latency of the network thread. If // this node is overloaded it won't respond to pings quickly and the // remote node can avoid sending us more work, like chain download // requests. // // The nonce stops the remote getting confused between different // pings: without it, if the remote node sends a ping once per // second and this node takes 5 seconds to respond to each, the 5th // ping the remote sends would appear to return very quickly. connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } } else if (strCommand == NetMsgType::PONG) { int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; if (nAvail >= sizeof(nonce)) { vRecv >> nonce; // Only process pong message if there is an outstanding ping (old // ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { // Matching pong received, this ping is no longer // outstanding bPingFinished = true; int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; pfrom->nMinPingUsecTime = std::min( pfrom->nMinPingUsecTime.load(), pingUsecTime); } else { // This should never happen sProblem = "Timing mishap"; } } else { // Nonce mismatches are normal when pings are overlapping sProblem = "Nonce mismatch"; if (nonce == 0) { // This is most likely a bug in another implementation // somewhere; cancel this ping bPingFinished = true; sProblem = "Nonce zero"; } } } else { sProblem = "Unsolicited pong without ping"; } } else { // This is most likely a bug in another implementation somewhere; // cancel this ping bPingFinished = true; sProblem = "Short payload"; } if (!(sProblem.empty())) { LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n", pfrom->GetId(), sProblem, pfrom->nPingNonceSent, nonce, nAvail); } if (bPingFinished) { pfrom->nPingNonceSent = 0; } } else if (strCommand == NetMsgType::FILTERLOAD) { CBloomFilter filter; vRecv >> filter; if (!filter.IsWithinSizeConstraints()) { // There is no excuse for sending a too-large filter LOCK(cs_main); Misbehaving(pfrom, 100, "oversized-bloom-filter"); } else { LOCK(pfrom->cs_filter); pfrom->pfilter.reset(new CBloomFilter(filter)); pfrom->pfilter->UpdateEmptyFull(); pfrom->fRelayTxes = true; } } else if (strCommand == NetMsgType::FILTERADD) { std::vector vData; vRecv >> vData; // Nodes must NEVER send a data item > 520 bytes (the max size for a // script data object, and thus, the maximum size any matched object can // have) in a filteradd message. bool bad = false; if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { bad = true; } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) { pfrom->pfilter->insert(vData); } else { bad = true; } } if (bad) { LOCK(cs_main); // The structure of this code doesn't really allow for a good error // code. We'll go generic. Misbehaving(pfrom, 100, "invalid-filteradd"); } } else if (strCommand == NetMsgType::FILTERCLEAR) { LOCK(pfrom->cs_filter); if (pfrom->GetLocalServices() & NODE_BLOOM) { pfrom->pfilter.reset(new CBloomFilter()); } pfrom->fRelayTxes = true; } else if (strCommand == NetMsgType::FEEFILTER) { Amount newFeeFilter = Amount::zero(); vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { { LOCK(pfrom->cs_feeFilter); pfrom->minFeeFilter = newFeeFilter; } LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId()); } } else if (strCommand == NetMsgType::NOTFOUND) { // We do not care about the NOTFOUND message, but logging an Unknown // Command message would be undesirable as we transmit it ourselves. } else { // Ignore unknown commands for extensibility LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId()); } return true; } static bool SendRejectsAndCheckIfBanned(CNode *pnode, CConnman *connman) { AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); for (const CBlockReject &reject : state.rejects) { connman->PushMessage( pnode, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); } state.rejects.clear(); if (state.fShouldBan) { state.fShouldBan = false; if (pnode->fWhitelisted) { LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString()); } else if (pnode->m_manual_connection) { LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString()); } else { pnode->fDisconnect = true; if (pnode->addr.IsLocal()) { LogPrintf("Warning: not banning local peer %s!\n", pnode->addr.ToString()); } else { connman->Ban(pnode->addr, BanReasonNodeMisbehaving); } } return true; } return false; } bool PeerLogicValidation::ProcessMessages(const Config &config, CNode *pfrom, std::atomic &interruptMsgProc) { const CChainParams &chainparams = config.GetChainParams(); // // Message format // (4) message start // (12) command // (4) size // (4) checksum // (x) data // bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) { ProcessGetData(config, pfrom, connman, interruptMsgProc); } if (pfrom->fDisconnect) { return false; } // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) { return true; } // Don't bother if send buffer is too full to respond anyway if (pfrom->fPauseSend) { return false; } std::list msgs; { LOCK(pfrom->cs_vProcessMsg); if (pfrom->vProcessMsg.empty()) { return false; } // Just take one message msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin()); pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE; pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize(); fMoreWork = !pfrom->vProcessMsg.empty(); } CNetMessage &msg(msgs.front()); msg.SetVersion(pfrom->GetRecvVersion()); // Scan for message start if (memcmp(std::begin(msg.hdr.pchMessageStart), std::begin(chainparams.NetMagic()), CMessageHeader::MESSAGE_START_SIZE) != 0) { LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->GetId()); // Make sure we ban where that come from for some time. connman->Ban(pfrom->addr, BanReasonNodeMisbehaving); pfrom->fDisconnect = true; return false; } // Read header CMessageHeader &hdr = msg.hdr; if (!hdr.IsValid(config)) { LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->GetId()); return fMoreWork; } std::string strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; // Checksum CDataStream &vRecv = msg.vRecv; const uint256 &hash = msg.GetMessageHash(); if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { LogPrint( BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s from " "peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, HexStr(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE), HexStr(hdr.pchChecksum, hdr.pchChecksum + CMessageHeader::CHECKSUM_SIZE), pfrom->GetId()); connman->Ban(pfrom->addr, BanReasonNodeMisbehaving); return fMoreWork; } // Process message bool fRet = false; try { fRet = ProcessMessage(config, pfrom, strCommand, vRecv, msg.nTime, connman, interruptMsgProc); if (interruptMsgProc) { return false; } if (!pfrom->vRecvGetData.empty()) { fMoreWork = true; } } catch (const std::ios_base::failure &e) { connman->PushMessage( pfrom, CNetMsgMaker(INIT_PROTO_VERSION) .Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv LogPrint( BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a " "message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) { // Allow exceptions from non-canonical encoding LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else { PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (const std::exception &e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(nullptr, "ProcessMessages()"); } if (!fRet) { LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->GetId()); } LOCK(cs_main); SendRejectsAndCheckIfBanned(pfrom, connman); return fMoreWork; } void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds) { AssertLockHeld(cs_main); CNodeState &state = *State(pto->GetId()); const CNetMsgMaker msgMaker(pto->GetSendVersion()); if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) { // This is an outbound peer subject to disconnection if they don't // announce a block with as much work as the current tip within // CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if their // chain has more work than ours, we should sync to it, unless it's // invalid, in which case we should find that out and disconnect from // them elsewhere). if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork) { if (state.m_chain_sync.m_timeout != 0) { state.m_chain_sync.m_timeout = 0; state.m_chain_sync.m_work_header = nullptr; state.m_chain_sync.m_sent_getheaders = false; } } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) { // Our best block known by this peer is behind our tip, and we're // either noticing that for the first time, OR this peer was able to // catch up to some earlier point where we checked against our tip. // Either way, set a new timeout based on current tip. state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT; state.m_chain_sync.m_work_header = chainActive.Tip(); state.m_chain_sync.m_sent_getheaders = false; } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) { // No evidence yet that our peer has synced to a chain with work // equal to that of our tip, when we first detected it was behind. // Send a single getheaders message to give the peer a chance to // update us. if (state.m_chain_sync.m_sent_getheaders) { // They've run out of time to catch up! LogPrintf( "Disconnecting outbound peer %d for old chain, best known " "block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : ""); pto->fDisconnect = true; } else { assert(state.m_chain_sync.m_work_header); LogPrint( BCLog::NET, "sending getheaders to outbound peer=%d to verify chain " "work (current best known block:%s, benchmark blockhash: " "%s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "", state.m_chain_sync.m_work_header->GetBlockHash() .ToString()); connman->PushMessage( pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator( state.m_chain_sync.m_work_header->pprev), uint256())); state.m_chain_sync.m_sent_getheaders = true; // 2 minutes constexpr int64_t HEADERS_RESPONSE_TIME = 120; // Bump the timeout to allow a response, which could clear the // timeout (if the response shows the peer has synced), reset // the timeout (if the peer syncs to the required work but not // to our tip), or result in disconnect (if we advance to the // timeout and pindexBestKnownBlock has not sufficiently // progressed) state.m_chain_sync.m_timeout = time_in_seconds + HEADERS_RESPONSE_TIME; } } } } void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) { // Check whether we have too many outbound peers int extra_peers = connman->GetExtraOutboundCount(); if (extra_peers <= 0) { return; } // If we have more outbound peers than we target, disconnect one. // Pick the outbound peer that least recently announced us a new block, with // ties broken by choosing the more recent connection (higher node id) NodeId worst_peer = -1; int64_t oldest_block_announcement = std::numeric_limits::max(); LOCK(cs_main); connman->ForEachNode([&](CNode *pnode) { AssertLockHeld(cs_main); // Ignore non-outbound peers, or nodes marked for disconnect already if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) { return; } CNodeState *state = State(pnode->GetId()); if (state == nullptr) { // shouldn't be possible, but just in case return; } // Don't evict our protected peers if (state->m_chain_sync.m_protect) { return; } if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) { worst_peer = pnode->GetId(); oldest_block_announcement = state->m_last_block_announcement; } }); if (worst_peer == -1) { return; } bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) { AssertLockHeld(cs_main); // Only disconnect a peer that has been connected to us for some // reasonable fraction of our check-frequency, to give it time for new // information to have arrived. // Also don't disconnect any peer we're trying to download a block from. CNodeState &state = *State(pnode->GetId()); if (time_in_seconds - pnode->nTimeConnected > MINIMUM_CONNECT_TIME && state.nBlocksInFlight == 0) { LogPrint(BCLog::NET, "disconnecting extra outbound peer=%d (last block " "announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement); pnode->fDisconnect = true; return true; } else { LogPrint(BCLog::NET, "keeping outbound peer=%d chosen for eviction " "(connect time: %d, blocks_in_flight: %d)\n", pnode->GetId(), pnode->nTimeConnected, state.nBlocksInFlight); return false; } }); if (disconnected) { // If we disconnected an extra peer, that means we successfully // connected to at least one peer after the last time we detected a // stale tip. Don't try any more extra peers until we next detect a // stale tip, to limit the load we put on the network from these extra // connections. connman->SetTryNewOutboundPeer(false); } } void PeerLogicValidation::CheckForStaleTipAndEvictPeers( const Consensus::Params &consensusParams) { if (connman == nullptr) { return; } int64_t time_in_seconds = GetTime(); EvictExtraOutboundPeers(time_in_seconds); if (time_in_seconds <= m_stale_tip_check_time) { return; } LOCK(cs_main); // Check whether our tip is stale, and if so, allow using an extra outbound // peer. if (TipMayBeStale(consensusParams)) { LogPrintf("Potential stale tip detected, will try using extra outbound " "peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update); connman->SetTryNewOutboundPeer(true); } else if (connman->GetTryNewOutboundPeer()) { connman->SetTryNewOutboundPeer(false); } m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; } namespace { class CompareInvMempoolOrder { CTxMemPool *mp; public: explicit CompareInvMempoolOrder(CTxMemPool *_mempool) { mp = _mempool; } bool operator()(std::set::iterator a, std::set::iterator b) { /* As std::make_heap produces a max-heap, we want the entries with the * fewest ancestors/highest fee to sort later. */ return mp->CompareDepthAndScore(*b, *a); } }; -} +} // namespace bool PeerLogicValidation::SendMessages(const Config &config, CNode *pto, std::atomic &interruptMsgProc) { const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); // Don't send anything until the version handshake is complete if (!pto->fSuccessfullyConnected || pto->fDisconnect) { return true; } // If we get here, the outgoing message serialization version is set and // can't change. const CNetMsgMaker msgMaker(pto->GetSendVersion()); // // Message: ping // bool pingSend = false; if (pto->fPingQueued) { // RPC ping request by user pingSend = true; } if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { // Ping automatically sent as a latency probe & keepalive. pingSend = true; } if (pingSend) { uint64_t nonce = 0; while (nonce == 0) { GetRandBytes((uint8_t *)&nonce, sizeof(nonce)); } pto->fPingQueued = false; pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { // Peer is too old to support ping command with nonce, pong will // never arrive. pto->nPingNonceSent = 0; connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING)); } } // Acquire cs_main for IsInitialBlockDownload() and CNodeState() TRY_LOCK(cs_main, lockMain); if (!lockMain) { return true; } if (SendRejectsAndCheckIfBanned(pto, connman)) { return true; } CNodeState &state = *State(pto->GetId()); // Address refresh broadcast int64_t nNow = GetTimeMicros(); if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) { AdvertiseLocal(pto); pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); } // // Message: addr // if (pto->nNextAddrSend < nNow) { pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL); std::vector vAddr; vAddr.reserve(pto->vAddrToSend.size()); for (const CAddress &addr : pto->vAddrToSend) { if (!pto->addrKnown.contains(addr.GetKey())) { pto->addrKnown.insert(addr.GetKey()); vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { connman->PushMessage( pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); } // we only send the big addr message once if (pto->vAddrToSend.capacity() > 40) { pto->vAddrToSend.shrink_to_fit(); } } // Start block sync if (pindexBestHeader == nullptr) { pindexBestHeader = chainActive.Tip(); } // Download if this is a nice peer, or we have no nice peers and this one // might do. bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { // Only actively request headers from a single peer, unless we're close // to today. if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; state.nHeadersSyncTimeout = GetTimeMicros() + HEADERS_DOWNLOAD_TIMEOUT_BASE + HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER * (GetAdjustedTime() - pindexBestHeader->GetBlockTime()) / (consensusParams.nPowTargetSpacing); nSyncStarted++; const CBlockIndex *pindexStart = pindexBestHeader; /** * If possible, start at the block preceding the currently best * known header. This ensures that we always get a non-empty list of * headers back as long as the peer is up-to-date. With a non-empty * response, we can initialise the peer's known best block. This * wouldn't be possible if we requested starting at pindexBestHeader * and got back an empty response. */ if (pindexStart->pprev) { pindexStart = pindexStart->pprev; } LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight); connman->PushMessage( pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); } } // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet transactions // become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { GetMainSignals().Broadcast(nTimeBestReceived, connman); } // // Try sending block announcements via headers // { // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our list of block // hashes we're relaying, and our peer wants headers announcements, then // find the first header not yet known to our peer but would connect, // and send. If no header would connect, or if we have too many blocks, // or if the peer doesn't want headers, just add all to the inv queue. LOCK(pto->cs_inventory); std::vector vHeaders; bool fRevertToInv = ((!state.fPreferHeaders && (!state.fPreferHeaderAndIDs || pto->vBlockHashesToAnnounce.size() > 1)) || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); // last header queued for delivery const CBlockIndex *pBestIndex = nullptr; // ensure pindexBestKnownBlock is up-to-date ProcessBlockAvailability(pto->GetId()); if (!fRevertToInv) { bool fFoundStartingHeader = false; // Try to find first header that our peer doesn't have, and then // send all headers past that one. If we come across an headers that // aren't on chainActive, give up. for (const uint256 &hash : pto->vBlockHashesToAnnounce) { const CBlockIndex *pindex = LookupBlockIndex(hash); assert(pindex); if (chainActive[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; break; } if (pBestIndex != nullptr && pindex->pprev != pBestIndex) { // This means that the list of blocks to announce don't // connect to each other. This shouldn't really be possible // to hit during regular operation (because reorgs should // take us to a chain that has some block not on the prior // chain, which should be caught by the prior check), but // one way this could happen is by using invalidateblock / // reconsiderblock repeatedly on the tip, causing it to be // added multiple times to vBlockHashesToAnnounce. Robustly // deal with this rare situation by reverting to an inv. fRevertToInv = true; break; } pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message vHeaders.push_back(pindex->GetBlockHeader()); } else if (PeerHasHeader(&state, pindex)) { // Keep looking for the first new block. continue; } else if (pindex->pprev == nullptr || PeerHasHeader(&state, pindex->pprev)) { // Peer doesn't have this header but they do have the prior // one. // Start sending headers. fFoundStartingHeader = true; vHeaders.push_back(pindex->GetBlockHeader()); } else { // Peer doesn't have this header or the prior one -- // nothing will connect, so bail out. fRevertToInv = true; break; } } } if (!fRevertToInv && !vHeaders.empty()) { if (vHeaders.size() == 1 && state.fPreferHeaderAndIDs) { // We only send up to 1 block as header-and-ids, as otherwise // probably means we're doing an initial-ish-sync or they're // slow. LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->GetId()); int nSendFlags = 0; bool fGotBlockFromCache = false; { LOCK(cs_most_recent_block); if (most_recent_block_hash == pBestIndex->GetBlockHash()) { CBlockHeaderAndShortTxIDs cmpctblock( *most_recent_block); connman->PushMessage( pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); fGotBlockFromCache = true; } } if (!fGotBlockFromCache) { CBlock block; bool ret = ReadBlockFromDisk(block, pBestIndex, config); assert(ret); CBlockHeaderAndShortTxIDs cmpctblock(block); connman->PushMessage( pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { if (vHeaders.size() > 1) { LogPrint(BCLog::NET, "%s: %u headers, range (%s, %s), to peer=%d\n", __func__, vHeaders.size(), vHeaders.front().GetHash().ToString(), vHeaders.back().GetHash().ToString(), pto->GetId()); } else { LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->GetId()); } connman->PushMessage( pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); state.pindexBestHeaderSent = pBestIndex; } else { fRevertToInv = true; } } if (fRevertToInv) { // If falling back to using an inv, just try to inv the tip. The // last entry in vBlockHashesToAnnounce was our tip at some point in // the past. if (!pto->vBlockHashesToAnnounce.empty()) { const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); const CBlockIndex *pindex = LookupBlockIndex(hashToAnnounce); assert(pindex); // Warn if we're announcing a block that is not on the main // chain. This should be very rare and could be optimized out. // Just log for now. if (chainActive[pindex->nHeight] != pindex) { LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n", hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString()); } // If the peer's chain has this block, don't inv it back. if (!PeerHasHeader(&state, pindex)) { pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__, pto->GetId(), hashToAnnounce.ToString()); } } } pto->vBlockHashesToAnnounce.clear(); } // // Message: inventory // std::vector vInv; { LOCK(pto->cs_inventory); vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX_PER_MB * config.GetMaxBlockSize() / 1000000)); // Add blocks for (const uint256 &hash : pto->vInventoryBlockToSend) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } pto->vInventoryBlockToSend.clear(); // Check whether periodic sends should happen bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; // Use half the delay for outbound peers, as there is less privacy // concern for them. pto->nNextInvSend = PoissonNextSend( nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } // Time to send but the peer has requested we not relay transactions. if (fSendTrickle) { LOCK(pto->cs_filter); if (!pto->fRelayTxes) { pto->setInventoryTxToSend.clear(); } } // Respond to BIP35 mempool requests if (fSendTrickle && pto->fSendMempool) { auto vtxinfo = g_mempool.infoAll(); pto->fSendMempool = false; Amount filterrate = Amount::zero(); { LOCK(pto->cs_feeFilter); filterrate = pto->minFeeFilter; } LOCK(pto->cs_filter); for (const auto &txinfo : vtxinfo) { const uint256 &txid = txinfo.tx->GetId(); CInv inv(MSG_TX, txid); pto->setInventoryTxToSend.erase(txid); if (filterrate != Amount::zero() && txinfo.feeRate.GetFeePerK() < filterrate) { continue; } if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) { continue; } pto->filterInventoryKnown.insert(txid); vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } pto->timeLastMempoolReq = GetTime(); } // Determine transactions to relay if (fSendTrickle) { // Produce a vector with all candidates for sending std::vector::iterator> vInvTx; vInvTx.reserve(pto->setInventoryTxToSend.size()); for (std::set::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { vInvTx.push_back(it); } Amount filterrate = Amount::zero(); { LOCK(pto->cs_feeFilter); filterrate = pto->minFeeFilter; } // Topologically and fee-rate sort the inventory we send for privacy // and priority reasons. A heap is used so that not all items need // sorting if only a few are being sent. CompareInvMempoolOrder compareInvMempoolOrder(&g_mempool); std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); // No reason to drain out at many times the network's capacity, // especially since we have many peers and some will draw much // shorter delays. unsigned int nRelayedTransactions = 0; LOCK(pto->cs_filter); while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX_PER_MB * config.GetMaxBlockSize() / 1000000) { // Fetch the top element from the heap std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); std::set::iterator it = vInvTx.back(); vInvTx.pop_back(); uint256 hash = *it; // Remove it from the to-be-sent set pto->setInventoryTxToSend.erase(it); // Check if not in the filter already if (pto->filterInventoryKnown.contains(hash)) { continue; } // Not in the mempool anymore? don't bother sending it. auto txinfo = g_mempool.info(hash); if (!txinfo.tx) { continue; } if (filterrate != Amount::zero() && txinfo.feeRate.GetFeePerK() < filterrate) { continue; } if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) { continue; } // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; { // Expire old relay messages while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow) { mapRelay.erase(vRelayExpiration.front().second); vRelayExpiration.pop_front(); } auto ret = mapRelay.insert( std::make_pair(hash, std::move(txinfo.tx))); if (ret.second) { vRelayExpiration.push_back(std::make_pair( nNow + 15 * 60 * 1000000, ret.first)); } } if (vInv.size() == MAX_INV_SZ) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } pto->filterInventoryKnown.insert(hash); } } } if (!vInv.empty()) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); } // Detect whether we're stalling nNow = GetTimeMicros(); if (state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { // Stalling only triggers when the block download window cannot move. // During normal steady state, the download window should be much larger // than the to-be-downloaded set of blocks, so disconnection should only // happen during initial block download. LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->GetId()); pto->fDisconnect = true; return true; } // In case there is a block that has been in flight from this peer for 2 + // 0.5 * N times the block interval (with N the number of peers from which // we're downloading validated blocks), disconnect due to timeout. We // compensate for other peers to prevent killing off peers due to our own // downstream link being saturated. We only count validated in-flight blocks // so peers can't advertise non-existing block hashes to unreasonably // increase our timeout. if (state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { LogPrintf("Timeout downloading block %s from peer=%d, " "disconnecting\n", queuedBlock.hash.ToString(), pto->GetId()); pto->fDisconnect = true; return true; } } // Check for headers sync timeouts if (state.fSyncStarted && state.nHeadersSyncTimeout < std::numeric_limits::max()) { // Detect whether this is a stalling initial-headers-sync peer if (pindexBestHeader->GetBlockTime() <= GetAdjustedTime() - 24 * 60 * 60) { if (nNow > state.nHeadersSyncTimeout && nSyncStarted == 1 && (nPreferredDownload - state.fPreferredDownload >= 1)) { // Disconnect a (non-whitelisted) peer if it is our only sync // peer, and we have others we could be using instead. // Note: If all our peers are inbound, then we won't disconnect // our sync peer for stalling; we have bigger problems if we // can't get any outbound peers. if (!pto->fWhitelisted) { LogPrintf("Timeout downloading headers from peer=%d, " "disconnecting\n", pto->GetId()); pto->fDisconnect = true; return true; } else { LogPrintf("Timeout downloading headers from whitelisted " "peer=%d, not disconnecting\n", pto->GetId()); // Reset the headers sync state so that we have a chance to // try downloading from a different peer. // Note: this will also result in at least one more // getheaders message to be sent to this peer (eventually). state.fSyncStarted = false; nSyncStarted--; state.nHeadersSyncTimeout = 0; } } } else { // After we've caught up once, reset the timeout so we can't trigger // disconnect later. state.nHeadersSyncTimeout = std::numeric_limits::max(); } } // Check that outbound peers have reasonable chains GetTime() is used by // this anti-DoS logic so we can test this using mocktime. ConsiderEviction(pto, GetTime()); // // Message: getdata (blocks) // std::vector vGetData; if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); for (const CBlockIndex *pindex : vToDownload) { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(config, pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->GetId()); } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { State(staller)->nStallingSince = nNow; LogPrint(BCLog::NET, "Stall started peer=%d\n", staller); } } } // // Message: getdata (non-blocks) // while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv &inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) { LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->GetId()); vGetData.push_back(inv); if (vGetData.size() >= 1000) { connman->PushMessage( pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); vGetData.clear(); } } else { // If we're not going to ask, don't expect a response. pto->setAskFor.erase(inv.hash); } pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) { connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } // // Message: feefilter // // We don't want white listed peers to filter txs to us if we have // -whitelistforcerelay if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && !(pto->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { Amount currentFilter = g_mempool .GetMinFee( gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000) .GetFeePerK(); int64_t timeNow = GetTimeMicros(); if (timeNow > pto->nextSendTimeFeeFilter) { static CFeeRate default_feerate = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB); static FeeFilterRounder filterRounder(default_feerate); Amount filterToSend = filterRounder.round(currentFilter); // If we don't allow free transactions, then we always have a fee // filter of at least minRelayTxFee if (gArgs.GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0) { filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); } if (filterToSend != pto->lastSentFeeFilter) { connman->PushMessage( pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); pto->lastSentFeeFilter = filterToSend; } pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); } // If the fee filter has changed substantially and it's still more than // MAX_FEEFILTER_CHANGE_DELAY until scheduled broadcast, then move the // broadcast to within MAX_FEEFILTER_CHANGE_DELAY. else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter && (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) { pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000; } } return true; } class CNetProcessingCleanup { public: CNetProcessingCleanup() {} ~CNetProcessingCleanup() { // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); } } instance_of_cnetprocessingcleanup; diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index fde5f0fa1..226ef982f 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -1,313 +1,313 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_BITCOINGUI_H #define BITCOIN_QT_BITCOINGUI_H #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include class ClientModel; class NetworkStyle; class Notificator; class OptionsModel; class PlatformStyle; class RPCConsole; class SendCoinsRecipient; class UnitDisplayStatusBarControl; class WalletFrame; class WalletModel; class HelpMessageDialog; class ModalOverlay; class Config; namespace interfaces { class Handler; class Node; -} +} // namespace interfaces QT_BEGIN_NAMESPACE class QAction; class QComboBox; class QProgressBar; class QProgressDialog; QT_END_NAMESPACE /** * Bitcoin GUI main class. This class represents the main window of the Bitcoin * UI. It communicates with both the client and wallet models to give the user * an up-to-date view of the current core state. */ class BitcoinGUI : public QMainWindow { Q_OBJECT public: static const std::string DEFAULT_UIPLATFORM; explicit BitcoinGUI(interfaces::Node &node, const Config *, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** * Set the client model. * The client model represents the part of the core that communicates with * the P2P network, and is wallet-agnostic. */ void setClientModel(ClientModel *clientModel); #ifdef ENABLE_WALLET /** * Set the wallet model. * The wallet model represents a bitcoin wallet, and offers access to the * list of transactions, address book and sending functionality. */ bool addWallet(WalletModel *walletModel); void removeAllWallets(); #endif // ENABLE_WALLET bool enableWallet = false; protected: void changeEvent(QEvent *e) override; void closeEvent(QCloseEvent *event) override; void showEvent(QShowEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; void dropEvent(QDropEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override; private: interfaces::Node &m_node; std::unique_ptr m_handler_message_box; std::unique_ptr m_handler_question; ClientModel *clientModel = nullptr; WalletFrame *walletFrame = nullptr; UnitDisplayStatusBarControl *unitDisplayControl = nullptr; QLabel *labelWalletEncryptionIcon = nullptr; QLabel *labelWalletHDStatusIcon = nullptr; QLabel *connectionsControl = nullptr; QLabel *labelBlocksIcon = nullptr; QLabel *progressBarLabel = nullptr; QProgressBar *progressBar = nullptr; QProgressDialog *progressDialog = nullptr; QMenuBar *appMenuBar = nullptr; QToolBar *appToolBar = nullptr; QAction *overviewAction = nullptr; QAction *historyAction = nullptr; QAction *quitAction = nullptr; QAction *sendCoinsAction = nullptr; QAction *sendCoinsMenuAction = nullptr; QAction *usedSendingAddressesAction = nullptr; QAction *usedReceivingAddressesAction = nullptr; QAction *signMessageAction = nullptr; QAction *verifyMessageAction = nullptr; QAction *aboutAction = nullptr; QAction *receiveCoinsAction = nullptr; QAction *receiveCoinsMenuAction = nullptr; QAction *optionsAction = nullptr; QAction *toggleHideAction = nullptr; QAction *encryptWalletAction = nullptr; QAction *backupWalletAction = nullptr; QAction *changePassphraseAction = nullptr; QAction *aboutQtAction = nullptr; QAction *openRPCConsoleAction = nullptr; QAction *openAction = nullptr; QAction *showHelpMessageAction = nullptr; QLabel *m_wallet_selector_label = nullptr; QComboBox *m_wallet_selector = nullptr; QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; Notificator *notificator = nullptr; RPCConsole *rpcConsole = nullptr; HelpMessageDialog *helpMessageDialog = nullptr; ModalOverlay *modalOverlay = nullptr; /** Keep track of previous number of blocks, to detect progress */ int prevBlocks = 0; int spinnerFrame = 0; const PlatformStyle *platformStyle; const Config *config; /** Create the main UI actions. */ void createActions(); /** Create the menu bar and sub-menus. */ void createMenuBar(); /** Create the toolbars */ void createToolBars(); /** Create system tray icon and notification */ void createTrayIcon(const NetworkStyle *networkStyle); /** Create system tray menu (or setup the dock menu) */ void createTrayIconMenu(); /** Enable or disable all wallet-related actions */ void setWalletActionsEnabled(bool enabled); /** Connect core signals to GUI client */ void subscribeToCoreSignals(); /** Disconnect core signals from GUI client */ void unsubscribeFromCoreSignals(); /** Update UI with latest network info from model. */ void updateNetworkState(); void updateHeadersSyncProgressLabel(); Q_SIGNALS: /** Signal raised when a URI was entered or dragged to the GUI */ void receivedURI(const QString &uri); public Q_SLOTS: /** Set number of connections shown in the UI */ void setNumConnections(int count); /** Set network state shown in the UI */ void setNetworkActive(bool networkActive); /** Set number of blocks and last block date shown in the UI */ void setNumBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, bool headers); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title @param[in] message the displayed text @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) @see CClientUIInterface::MessageBoxFlags @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) */ void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr); #ifdef ENABLE_WALLET bool setCurrentWallet(const QString &name); /** Set the UI status indicators based on the currently selected wallet. */ void updateWalletStatus(); private: /** Set the encryption status as shown in the UI. @param[in] status current encryption status @see WalletModel::EncryptionStatus */ void setEncryptionStatus(int status); /** Set the hd-enabled status as shown in the UI. @param[in] status current hd enabled status @see WalletModel::EncryptionStatus */ void setHDStatus(int hdEnabled); public Q_SLOTS: bool handlePaymentRequest(const SendCoinsRecipient &recipient); /** Show incoming transaction notification for new transactions. */ void incomingTransaction(const QString &date, int unit, const Amount amount, const QString &type, const QString &address, const QString &label, const QString &walletName); #endif // ENABLE_WALLET private Q_SLOTS: #ifdef ENABLE_WALLET /** Switch to overview (home) page */ void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ void gotoSendCoinsPage(QString addr = ""); /** Show Sign/Verify Message dialog and switch to sign message tab */ void gotoSignMessageTab(QString addr = ""); /** Show Sign/Verify Message dialog and switch to verify message tab */ void gotoVerifyMessageTab(QString addr = ""); /** Show open dialog */ void openClicked(); #endif // ENABLE_WALLET /** Show configuration dialog */ void optionsClicked(); /** Show about dialog */ void aboutClicked(); /** Show debug window */ void showDebugWindow(); /** Show debug window and set focus to the console */ void showDebugWindowActivateConsole(); /** Show help message dialog */ void showHelpMessageClicked(); #ifndef Q_OS_MAC /** Handle tray icon clicked */ void trayIconActivated(QSystemTrayIcon::ActivationReason reason); #endif /** Show window if hidden, unminimize when minimized, rise when obscured or * show if hidden and fToggleHidden is true */ void showNormalIfMinimized(bool fToggleHidden = false); /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); /** called by a timer to check if fRequestShutdown has been set **/ void detectShutdown(); /** Show progress dialog e.g. for verifychain */ void showProgress(const QString &title, int nProgress); /** When hideTrayIcon setting is changed in OptionsModel hide or show the * icon accordingly. */ void setTrayIconVisible(bool); /** Toggle networking */ void toggleNetworkActive(); void showModalOverlay(); }; class UnitDisplayStatusBarControl : public QLabel { Q_OBJECT public: explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle); /** Lets the control know about the Options Model (and its signals) */ void setOptionsModel(OptionsModel *optionsModel); protected: /** So that it responds to left-button clicks */ void mousePressEvent(QMouseEvent *event) override; private: OptionsModel *optionsModel; QMenu *menu; /** Shows context menu with Display Unit options by the mouse coordinates */ void onDisplayUnitsClicked(const QPoint &point); /** Creates context menu, its actions, and wires up all the relevant signals * for mouse events. */ void createContextMenu(); private Q_SLOTS: /** When Display Units are changed on OptionsModel it will refresh the * display text of the control on the status bar */ void updateDisplayUnit(int newUnits); /** Tells underlying optionsModel to update its current display unit. */ void onMenuSelection(QAction *action); }; #endif // BITCOIN_QT_BITCOINGUI_H diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 87c74e448..dd64ec2c6 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -1,117 +1,117 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_CLIENTMODEL_H #define BITCOIN_QT_CLIENTMODEL_H #include #include #include #include class BanTableModel; class OptionsModel; class PeerTableModel; class CBlockIndex; namespace interfaces { class Handler; class Node; -} +} // namespace interfaces QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE enum class BlockSource { NONE, REINDEX, DISK, NETWORK }; enum NumConnections { CONNECTIONS_NONE = 0, CONNECTIONS_IN = (1U << 0), CONNECTIONS_OUT = (1U << 1), CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), }; /** Model for Bitcoin network client. */ class ClientModel : public QObject { Q_OBJECT public: explicit ClientModel(interfaces::Node &node, OptionsModel *optionsModel, QObject *parent = 0); ~ClientModel(); interfaces::Node &node() const { return m_node; } OptionsModel *getOptionsModel(); PeerTableModel *getPeerTableModel(); BanTableModel *getBanTableModel(); //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; int getHeaderTipHeight() const; int64_t getHeaderTipTime() const; //! Returns enum BlockSource of the current importing/syncing state enum BlockSource getBlockSource() const; //! Return warnings to be displayed in status bar QString getStatusBarWarnings() const; QString formatFullVersion() const; QString formatSubVersion() const; bool isReleaseVersion() const; QString formatClientStartupTime() const; QString dataDir() const; QString blocksDir() const; // caches for the best header mutable std::atomic cachedBestHeaderHeight; mutable std::atomic cachedBestHeaderTime; private: interfaces::Node &m_node; std::unique_ptr m_handler_show_progress; std::unique_ptr m_handler_notify_num_connections_changed; std::unique_ptr m_handler_notify_network_active_changed; std::unique_ptr m_handler_notify_alert_changed; std::unique_ptr m_handler_banned_list_changed; std::unique_ptr m_handler_notify_block_tip; std::unique_ptr m_handler_notify_header_tip; OptionsModel *optionsModel; PeerTableModel *peerTableModel; BanTableModel *banTableModel; QTimer *pollTimer; void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); Q_SIGNALS: void numConnectionsChanged(int count); void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, bool header); void mempoolSizeChanged(long count, size_t mempoolSizeInBytes); void networkActiveChanged(bool networkActive); void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); //! Fired when a message should be reported to the user void message(const QString &title, const QString &message, unsigned int style); // Show progress dialog e.g. for verifychain void showProgress(const QString &title, int nProgress); public Q_SLOTS: void updateTimer(); void updateNumConnections(int numConnections); void updateNetworkActive(bool networkActive); void updateAlert(); void updateBanlist(); }; #endif // BITCOIN_QT_CLIENTMODEL_H diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 4971ad0b0..e8524de96 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -1,71 +1,71 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_SPLASHSCREEN_H #define BITCOIN_QT_SPLASHSCREEN_H #include #include #include class NetworkStyle; namespace interfaces { class Handler; class Node; class Wallet; -}; +}; // namespace interfaces /** Class for the splashscreen with information of the running client. * * @note this is intentionally not a QSplashScreen. Bitcoin Core initialization * can take a long time, and in that case a progress window that cannot be moved * around and minimized has turned out to be frustrating to the user. */ class SplashScreen : public QWidget { Q_OBJECT public: explicit SplashScreen(interfaces::Node &node, Qt::WindowFlags f, const NetworkStyle *networkStyle); ~SplashScreen(); protected: void paintEvent(QPaintEvent *event) override; void closeEvent(QCloseEvent *event) override; public Q_SLOTS: /** Slot to call finish() method as it's not defined as slot */ void slotFinish(QWidget *mainWin); /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); protected: bool eventFilter(QObject *obj, QEvent *ev) override; private: /** Connect core signals to splash screen */ void subscribeToCoreSignals(); /** Disconnect core signals to splash screen */ void unsubscribeFromCoreSignals(); /** Connect wallet signals to splash screen */ void ConnectWallet(std::unique_ptr wallet); QPixmap pixmap; QString curMessage; QColor curColor; int curAlignment; interfaces::Node &m_node; std::unique_ptr m_handler_init_message; std::unique_ptr m_handler_show_progress; std::unique_ptr m_handler_load_wallet; std::list> m_connected_wallets; std::list> m_connected_wallet_handlers; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index ca57f6225..7f7f64131 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -1,220 +1,220 @@ // Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include +#include #include #include