Skip to content

Commit 2fd565c

Browse files
committed
Design doc for contexts
1 parent 50e7e14 commit 2fd565c

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

designdocs/Contexts.md

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

0 commit comments

Comments
 (0)