mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2024-11-19 09:50:32 +01:00
105 lines
7.2 KiB
Markdown
105 lines
7.2 KiB
Markdown
# Design of API contexts
|
|
|
|
This design doc describes the Context class that is new in 0.13.
|
|
|
|
## Goals
|
|
|
|
- To centralise various bits of configuration that are presently duplicated throughout the API, such as directories for
|
|
storing files, the depth at which a tx is considered un-reorgable, the chosen network parameters and so on.
|
|
- To simplify bitcoinj programming in future.
|
|
- To avoid the temptation to overload NetworkParameters with tweakable bits and pieces of misc configuration.
|
|
- Unblock various bits of API evolution that are currently made difficult by the desire to avoid too many source code
|
|
changes by developers.
|
|
|
|
## Background
|
|
|
|
Since the very first version bitcoinj has had the concept of "network parameters": a class that wrapped various
|
|
constants and magic numbers that distinguish the Bitcoin main network from the test network, and later, from settings
|
|
meant only for unit tests and local regression testing.
|
|
|
|
However unlike many APIs, we have never had a more general notion of context and as the library has grown we have
|
|
ended up with an often confusing mishmash of duplicate settings and odd dependencies between objects. For example
|
|
several parts of the library want to throw away data once a transaction is confirmed enough that we don't expect it
|
|
to ever be re-orgd out of the chain, but there's no agreement on how deep that should be. The Wallet stores files, as
|
|
does the block store, but each component must be told where to put this data individually.
|
|
The problem gets worse on Android, where there are no JAR files and data must be shipped as external files. On this
|
|
platform components that want to load data files must be configured with a path to their files individually and
|
|
there's no central list of what components need this.
|
|
|
|
Another problem is that in a few places, we have APIs that are in wide usage but need to start accepting an explicit
|
|
component to allow the underlying code to evolve. Top of the list is TransactionConfidence. Consider
|
|
TransactionConfidence.getDepthInBlocks(). This method takes no parameters and thus practically requires that the
|
|
confidence object for every transaction in a wallet be touched on every single block in order to update its internal
|
|
counter. A better approach would be for it to just record the height it appeared at and then take an AbstractBlockChain
|
|
as a parameter (and/or explicit height) then do the subtraction. But often this method is called in places far away
|
|
from the last reference to a block chain and so this will be a painful API change. Ideally we could spread it out over
|
|
a release or two, to give developers time to update their code.
|
|
|
|
An even bigger issue is Transaction.getConfidence() itself. We would like to rewrite the Wallet so it no longer stores
|
|
Transaction objects at all. But this would be a major API break, because apps often want to know about the confidence
|
|
of a transaction and currently the only way to obtain this is via the getConfidence() method. The TxConfidenceTable class
|
|
(renamed from MemoryPool) acts as a global map of txhash to confidence object, but we can't adjust the prototype of
|
|
Transaction.getConfidence() to take one without breaking lots of code.
|
|
|
|
This proliferation of global variables makes it harder for developers to use multiple instances of bitcoinj
|
|
simultaneously, for example, to do cross-chain trading of different cryptocurrencies against each other.
|
|
|
|
Finally, several bitcoinj objects currently need to be plugged together in ways that aren't always obvious for full
|
|
functionality to work. The constructors try to guide the developer but it's still a common source of mistakes.
|
|
|
|
We can resolve these problems by introducing a notion of a global Context object, used in the same places and ways as
|
|
NetworkParameters is today.
|
|
|
|
## Context object
|
|
|
|
The Context class is very simple. It is an immutable class that simply holds configuration data and references to other
|
|
objects. For now, we do not allow on-the-fly reconfiguration of the data stored within it. This is to simplify the
|
|
implementation code.
|
|
|
|
## Alternatives considered
|
|
|
|
Some code bases, when faced with similar problems to the above, use a dependency injection container. These pieces
|
|
of software effectively replace the "new" keyword and handle all object creation themselves, then wire objects together
|
|
based on annotations and centralised, explicit configuration.
|
|
|
|
Dependency injection would seem to be an attractive solution, but:
|
|
|
|
* Experience of using Guice inside Google leads me to believe it will result in confusing code that breaks IDE navigation
|
|
features and is hostile to the inexperienced code reader.
|
|
* Guice effectively changes the Java language and that makes it harder for people to contribute. There may be DI
|
|
frameworks that are less aggressive, but I don't know of any.
|
|
* DI often relies heavily on reflection and even runtime code synthesis, which we wish to avoid for performance reasons
|
|
and to avoid complicating ProGuard configuration and transpilation.
|
|
* DI is effectively just a complicated and indirect means of having a global context object: doing it directly makes the
|
|
code clearer and avoids the need for developers to learn new things.
|
|
|
|
## Transition plan
|
|
|
|
NetworkParameters appears everywhere in the bitcoinj API, and so introducing Context will have a major impact on it
|
|
as well. We aim to keep API churn under control, to avoid losing developers across difficult upgrades. As such,
|
|
Context will be phased in gradually over one or two releases.
|
|
|
|
We will follow these stages:
|
|
|
|
1. Context starts out by wrapping NetworkParameters, TxConfidenceTable and the "event horizon" (the number of blocks
|
|
after which we assume re-orgs cannot happen).
|
|
2. The construction of a Context object puts a reference to itself into a thread local storage slot. A static method
|
|
is provided which retrieves this, as well as another that either retrieves _or creates_ a new Context. This second
|
|
method is placed in the constructors of key classes like the Wallet or the block chain, and provides backwards
|
|
compatibility for developers. A log message is printed advising developers to update their code to create a Context
|
|
themselves. Attempting to use two instances of the library with different objects or NetworkParameters from the same
|
|
thread may have complications or not work properly during this stage.
|
|
3. Classes that currently take NetworkParameters are augmented with new constructors that take Contexts instead. The
|
|
old c'tors simply check the NetworkParameters they are given matches the Context's own view and then call into the
|
|
new c'tors. An exception is thrown if they don't match.
|
|
4. Release notes describe how to set a context and propagate it between threads. Developers can start migration in the
|
|
0.13 release.
|
|
5. Internally, we start passing contexts through to objects that want one explicitly rather than relying on the thread
|
|
local storage slot.
|
|
6. We mark constructors that take NetworkParameters as deprecated with the javadocs changing to point devs to the
|
|
Context-taking equivalents.
|
|
7. In some future release, the deprecated methods are eventually removed, along with the Context thread local storage
|
|
slot and automated cross-thread propagation magic.
|
|
|
|
In parallel, global configuration will keep being moved into the Context class to make it more useful.
|