Developer Notes
Developer Notes
Developer Notes
===============
- [Developer Notes](#developer-notes)
- [Coding Style (General)](#coding-style-general)
- [Coding Style (C++)](#coding-style-c)
- [Coding Style (Python)](#coding-style-python)
- [Coding Style (Doxygen-compatible comments)](#coding-style-doxygen-
compatible-comments)
- [Generating Documentation](#generating-documentation)
- [Development tips and tricks](#development-tips-and-tricks)
- [Compiling for debugging](#compiling-for-debugging)
- [Compiling for gprof profiling](#compiling-for-gprof-profiling)
- [`debug.log`](#debuglog)
- [Signet, testnet, and regtest modes](#signet-testnet-and-regtest-modes)
- [DEBUG_LOCKORDER](#debug_lockorder)
- [Valgrind suppressions file](#valgrind-suppressions-file)
- [Compiling for test coverage](#compiling-for-test-coverage)
- [Performance profiling with perf](#performance-profiling-with-perf)
- [Sanitizers](#sanitizers)
- [Locking/mutex usage notes](#lockingmutex-usage-notes)
- [Threads](#threads)
- [Ignoring IDE/editor files](#ignoring-ideeditor-files)
- [Development guidelines](#development-guidelines)
- [General Bitcoin Core](#general-bitcoin-core)
- [Wallet](#wallet)
- [General C++](#general-c)
- [C++ data structures](#c-data-structures)
- [Strings and formatting](#strings-and-formatting)
- [Shadowing](#shadowing)
- [Threads and synchronization](#threads-and-synchronization)
- [Scripts](#scripts)
- [Shebang](#shebang)
- [Source code organization](#source-code-organization)
- [GUI](#gui)
- [Subtrees](#subtrees)
- [Upgrading LevelDB](#upgrading-leveldb)
- [File Descriptor Counts](#file-descriptor-counts)
- [Consensus Compatibility](#consensus-compatibility)
- [Scripted diffs](#scripted-diffs)
- [Suggestions and examples](#suggestions-and-examples)
- [Release notes](#release-notes)
- [RPC interface guidelines](#rpc-interface-guidelines)
- [Internal interface guidelines](#internal-interface-guidelines)
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, which is specified below. When writing patches, favor the new
style over attempting to mimic the surrounding style, except for move-only
commits.
Do not submit patches solely to modify the style of existing code.
- **Symbol naming conventions**. These are preferred in new code, but are not
required when doing so would need changes to significant pieces of existing
code.
- Variable (including function arguments) and namespace names are all lowercase
and may use `_` to
separate words (snake_case).
- Class member variables have a `m_` prefix.
- Global variables have a `g_` prefix.
- Compile-time constant names are all uppercase, and use `_` to separate words.
- Class names, function names, and method names are UpperCamelCase
(PascalCase). Do not prefix class names with `C`.
- 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.
- **Miscellaneous**
- `++i` is preferred over `i++`.
- `nullptr` is preferred over `NULL` or `(void*)0`.
- `static_assert` is preferred over `assert` where possible. Generally; compile-
time checking is preferred over run-time checking.
namespace foo {
class Class
{
std::string m_name;
public:
bool Function(const std::string& s, int n)
{
// Comment summarising what this section of code does
for (int i = 0; i < n; ++i) {
int total_sum = 0;
// When something fails, return early
if (!Something()) return false;
...
if (SomethingElse(i)) {
total_sum += ComputeSomething(g_count);
} else {
DoSomething(m_name, total_sum);
}
}
Refer to [/test/functional/README.md#style-guidelines]
(/test/functional/README.md#style-guidelines).
```c++
/**
* ... Description ...
*
* @param[in] arg1 input description...
* @param[in] arg2 input description...
* @param[out] arg3 output description...
* @return Return cases...
* @throws Error type and cases...
* @pre Pre-condition for function...
* @post Post-condition for function...
*/
bool function(int arg1, const char *arg2, std::string& arg3)
```
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
```
or
```c++
int var; //!< Description after the member
```
Also OK:
```c++
///
/// ... Description ...
///
bool function2(int arg1, const char *arg2)
```
Recommendations:
The documentation can be generated with `make docs` and cleaned up with `make
clean-docs`. The resulting files are located in `doc/doxygen/html`; open
`index.html` in that directory to view the homepage.
### `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.
If you are testing multi-machine code that needs to operate across the internet,
you can run with either the `-signet` or the `-testnet` config option to test
with "play bitcoins" on a test network.
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/](/test/functional) for tests that run in `-regtest` mode.
### DEBUG_LOCKORDER
The util file `src/util/check.h` offers helpers to protect against coding and
internal logic bugs. They must never be used to validate user, network or any
other input.
Valgrind is a programming tool for memory debugging, memory leak detection, and
profiling. The repo contains a Valgrind suppressions file
([`valgrind.supp`]
(https://github.com/bitcoin/bitcoin/blob/master/contrib/valgrind.supp))
which includes known Valgrind warnings in our dependencies that cannot be fixed
in-tree. Example use:
```shell
$ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin
$ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \
--show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite
$ valgrind -v --leak-check=full src/bitcoind -printtoconsole
$ ./test/functional/test_runner.py --valgrind
```
LCOV can be used to generate a test coverage report based upon `make check`
execution. LCOV must be installed on your system (e.g. the `lcov` package
on Debian/Ubuntu).
```shell
./configure --enable-lcov
make
make cov
# A coverage report will now be accessible at `./test_bitcoin.coverage/index.html`.
```
Profiling is a good way to get a precise idea of where time is being spent in
code. One tool for doing profiling on Linux platforms is called
[`perf`](http://www.brendangregg.com/perf.html), and has been integrated into
the functional test framework. Perf can observe a running process and sample
(at some frequency) where its execution is.
Certain kernel parameters may need to be set for perf to be able to inspect the
running process's stack.
```sh
$ sudo sysctl -w kernel.perf_event_paranoid=-1
$ sudo sysctl -w kernel.kptr_restrict=0
```
```sh
$ perf record \
-g --call-graph dwarf --per-thread -F 140 \
-p `pgrep bitcoind` -- sleep 60
```
```sh
perf report --stdio | c++filt | less
```
See the functional test documentation for how to invoke perf within tests.
### Sanitizers
Bitcoin Core 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
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)
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` (or use `--enable-debug`) to get lock order inconsistencies
reported in the `debug.log` file.
- [ThreadImport (`b-loadblk`)]
(https://doxygen.bitcoincore.org/init_8cpp.html#ae9e290a0e829ec0198518de2eda579d1)
: Loads blocks from `blk*.dat` files or `-loadblock=<file>` on startup.
- [ThreadScriptCheck (`b-scriptch.x`)]
(https://doxygen.bitcoincore.org/validation_8cpp.html#a925a33e7952a157922b0bbb8dab2
9a20)
: Parallel script validation threads for transactions in blocks.
- [ThreadHTTP (`b-http`)]
(https://doxygen.bitcoincore.org/httpserver_8cpp.html#abb9f6ea8819672bd9a62d3695070
709c)
: Libevent thread to listen for RPC and REST connections.
- [SchedulerThread (`b-scheduler`)]
(https://doxygen.bitcoincore.org/class_c_scheduler.html#a14d2800815da93577858ea078a
ed1fba)
: Does asynchronous background tasks like dumping wallet contents, dumping
addrman and running asynchronous validationinterface callbacks.
- [TorControlThread (`b-torcontrol`)]
(https://doxygen.bitcoincore.org/torcontrol_8cpp.html#a4faed3692d57a0d7bdbecf3b37f7
2de0)
: Libevent thread for tor connections.
- Net threads:
- [ThreadMessageHandler (`b-msghand`)]
(https://doxygen.bitcoincore.org/class_c_connman.html#aacdbb7148575a31bb33bc345e2bf
22a9)
: Application level message handling (sending and receiving). Almost
all net_processing and validation logic runs on this thread.
- [ThreadDNSAddressSeed (`b-dnsseed`)]
(https://doxygen.bitcoincore.org/class_c_connman.html#aa7c6970ed98a4a7bafbc071d2489
7d13)
: Loads addresses of peers from the DNS.
- [ThreadMapPort (`b-upnp`)]
(https://doxygen.bitcoincore.org/net_8cpp.html#a63f82a71c4169290c2db1651a9bbe249)
: Universal plug-and-play startup/shutdown.
- [ThreadSocketHandler (`b-net`)]
(https://doxygen.bitcoincore.org/class_c_connman.html#a765597cbfe99c083d8fa3d61bb46
4e34)
: Sends/Receives data from peers on port 8333.
- [ThreadOpenAddedConnections (`b-addcon`)]
(https://doxygen.bitcoincore.org/class_c_connman.html#a0b787caf95e52a346a2b31a580d6
0a62)
: Opens network connections to added nodes.
- [ThreadOpenConnections (`b-opencon`)]
(https://doxygen.bitcoincore.org/class_c_connman.html#a55e9feafc3bab78e5c9d408c207f
aa45)
: Initiates new connections to peers.
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
```
Then put your favourite tool's temporary filenames in that file, e.g.
```
# NetBeans
nbproject/
```
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
============================
- *Rationale*: RPC allows for better automatic testing. The test suite for
the GUI is very limited.
- *Rationale*: Makes sure that they pass thorough testing, and that the tester
will keep passing
on the master branch. Otherwise, all new pull requests will start failing the
tests, resulting in
confusion and mayhem.
Wallet
-------
General C++
-------------
For general C++ guidelines, you may refer to the [C++ Core
Guidelines](https://isocpp.github.io/CppCoreGuidelines/).
- *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.
- *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.
- Vector bounds checking is only enabled in debug mode. Do not rely on it.
```cpp
class A
{
uint32_t m_count{0};
}
```
- Prefer explicit constructions over implicit ones that rely on 'magical' C++
behavior.
```cpp
void Foo(Span<const int> data);
std::vector<int> vec{1,2,3};
Foo(vec);
```
```cpp
enum class Tabs {
INFO,
CONSOLE,
GRAPH,
PEERS
};
*Rationale*: The comment documents skipping `default:` label, and it complies with
`clang-format` rules. The assertion prevents firing of `-Wreturn-type` warning on
some compilers.
- Avoid using locale dependent functions if possible. You can use the provided
[`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh)
to check for accidental use of locale dependent functions.
- *Rationale*: Unnecessary locale dependence can cause bugs that are very tricky
to isolate and fix.
- *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out
to avoid confusion.
- Use `.c_str()` sparingly. Its only valid use is to pass C++ strings to C
functions that take NULL-terminated
strings.
- Do not use it when passing a sized array (so along with `.size()`). Use
`.data()` instead to get a pointer
to the raw data.
- In cases where do you call `.c_str()`, you might want to additionally check
that the string does not contain embedded '\0' characters, because
it will (necessarily) truncate the string. This might be used to hide parts of
the string from logging or to circumvent
checks. If a use of strings is sensitive to this, take care to check the string
for embedded NULL characters first
and reject it if there are any (see `ParsePrechecks` in `strencodings.cpp` for
an example).
Shadowing
--------------
Although the shadowing warning (`-Wshadow`) is not enabled by default (it prevents
issues arising
from using a different variable with the same name),
please name variables so that their names do not shadow variables defined in the
source code.
When using nested cycles, do not name the inner cycle variable the same as in
the upper cycle, etc.
- In functions that are declared separately from where they are defined, the
thread safety annotations should be added exclusively to the function
declaration. Annotations on the definition could lead to false positives
(lack of compile failure) at call sites between the two.
```C++
// txmempool.h
class CTxMemPool
{
public:
...
mutable RecursiveMutex cs;
...
void UpdateTransactionsFromBlock(...) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs);
...
}
// txmempool.cpp
void CTxMemPool::UpdateTransactionsFromBlock(...)
{
AssertLockHeld(::cs_main);
AssertLockHeld(cs);
...
}
```
```C++
// validation.h
class ChainstateManager
{
public:
...
bool ProcessNewBlock(...) LOCKS_EXCLUDED(::cs_main);
...
}
// validation.cpp
bool ChainstateManager::ProcessNewBlock(...)
{
AssertLockNotHeld(::cs_main);
...
LOCK(::cs_main);
...
}
```
- 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);
{
...
}
```
Scripts
--------------------------
### Shebang
`#!/usr/bin/env bash` searches the user's PATH to find the bash binary.
OK:
```bash
#!/usr/bin/env bash
```
Wrong:
```bash
#!/bin/bash
```
- 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.
- Every `.cpp` and `.h` file should `#include` every header file it directly uses
classes, functions or other
definitions from, even if those headers are already included indirectly through
other headers.
- Don't import anything into the global namespace (`using namespace ...`). Use
fully specified types such as `std::string`.
```c++
namespace mynamespace {
...
} // namespace mynamespace
namespace {
...
} // namespace
```
- 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
-----
- *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 `interfaces::Node` and `interfaces::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.
Subtrees
----------
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.
- src/leveldb
- Upstream at https://github.com/google/leveldb ; Maintained by Google, but
open important PRs to Core to avoid delay.
- **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb)
when
merging upstream changes to the LevelDB subtree.
- src/crc32c
- Used by leveldb for hardware acceleration of CRC32C checksums for data
integrity.
- Upstream at https://github.com/google/crc32c ; Maintained by Google.
- src/secp256k1
- Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained 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/bitcoin-core/univalue ; actively maintained by
Core contributors, deviates from upstream https://github.com/jgarzik/univalue
Upgrading LevelDB
---------------------
Extra care must be taken when upgrading LevelDB. This section explains issues
you must be aware of.
In addition to reviewing the upstream changes in `env_posix.cc`, you can use `lsof`
to
check this. For example, on Linux this command will show open `.ldb` file counts:
```bash
$ lsof -p $(pidof bitcoind) |\
awk 'BEGIN { fd=0; mem=0; } /ldb$/ { if ($4 == "mem") mem++; else fd++ } END
{ printf "mem = %s, fd = %s\n", mem, fd}'
mem = 119, fd = 0
```
The `mem` value shows how many files are mmap'ed, and the `fd` value shows you
many file descriptors these files are using. You should check that `fd` is a
small number (usually 0 on 64-bit hosts).
For example, if LevelDB had a bug that accidentally prevented a key from being
returned in an edge case, and that bug was fixed upstream, the bug "fix" would
be an incompatible consensus change. In this situation, the correct behavior
would be to revert the upstream fix before applying the updates to Bitcoin's
copy of LevelDB. In general, you should be wary of any upstream changes affecting
what data is returned from LevelDB queries.
Scripted diffs
--------------
For reformatting and refactoring commits where the changes can be easily automated
using a bash script, we use
scripted-diff commits. The bash script is included in the commit message and our CI
job checks that
the result of the script is identical to the commit. This aids reviewers since they
can verify that the script
does exactly what it is supposed to do. It is also helpful for rebasing (since the
same script can just be re-run
on the new master commit).
To create a scripted-diff:
- start the commit message with `scripted-diff:` (and then a description of the
diff on the same line)
- in the commit message include the bash script between lines containing just the
following text:
- `-BEGIN VERIFY SCRIPT-`
- `-END VERIFY SCRIPT-`
```bash
test/lint/commit-script-check.sh origin/master..HEAD
```
For efficient replacement scripts, reduce the selection to the files that
potentially need to be modified, so for
example, instead of a blanket `git ls-files src | xargs sed -i s/apple/orange/`,
use
`git grep -l apple src | xargs sed -i s/apple/orange/`.
```
git log --grep="-BEGIN VERIFY SCRIPT-"
```
Release notes
-------------
- Argument naming: use snake case `fee_delta` (and not, e.g. camel case `feeDelta`)
- Use the JSON parser for parsing, don't manually parse integers or strings from
arguments unless absolutely necessary.
- 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 to 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.
- Try not to overload methods on argument type. E.g. don't make `getblock(true)`
and `getblock("hash")`
do different things.
- *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.
- 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.
- Be aware of RPC method aliases and generally avoid registering the same
callback function pointer for different RPCs.
- *Rationale*: RPC methods registered with the same function pointer will be
considered aliases and only the first method name will show up in the
`help` RPC command list.
- Use *invalid* bech32 addresses (e.g. in the constant array `EXAMPLE_ADDRESS`) for
`RPCExamples` help documentation.
Interface classes are written in a particular style so node, wallet, and GUI
code doesn't need to run in the same process, and so the class declarations
work more easily with tools and libraries supporting interprocess
communication:
- Interface classes should be abstract and have methods that are [pure
virtual](https://en.cppreference.com/w/cpp/language/abstract_class). This
allows multiple implementations to inherit from the same interface class,
particularly so one implementation can execute functionality in the local
process, and other implementations can forward calls to remote processes.
Examples:
```c++
// Good: takes string argument and returns interface class pointer
virtual unique_ptr<interfaces::Wallet> loadWallet(std::string filename) = 0;
// Bad: returns CWallet reference that can't be used from another process
virtual CWallet& loadWallet(std::string filename) = 0;
```
```c++
// Good: accepts and returns primitive types
virtual bool findBlock(const uint256& hash, int& out_height, int64_t& out_time) =
0;
```c++
// Good: takes plain callback type and returns interface pointer
using TipChangedFn = std::function<void(int block_height, int64_t block_time)>;
virtual std::unique_ptr<interfaces::Handler> handleTipChanged(TipChangedFn fn) =
0;
Example:
```c++
// Good: error output param is last
virtual bool broadcastTransaction(const CTransactionRef& tx, CAmount max_fee,
std::string& error) = 0;
Example:
```c++
// Good: method names are unique
virtual bool disconnectByAddress(const CNetAddr& net_addr) = 0;
virtual bool disconnectById(NodeId id) = 0;
Examples:
```c++
// Good: lowerCamelCase method name
virtual void blockConnected(const CBlock& block, int height) = 0;