public abstract class AbstractBlockChain
extends java.lang.Object
An AbstractBlockChain holds a series of Block
objects, links them together, and knows how to verify that
the chain follows the rules of the NetworkParameters
for this chain.
It can be connected to a Wallet
, and also TransactionReceivedInBlockListener
s that can receive transactions and
notifications of re-organizations.
An AbstractBlockChain implementation must be connected to a BlockStore
implementation. The chain object
by itself doesn't store any data, that's delegated to the store. Which store you use is a decision best made by
reading the getting started guide, but briefly, fully validating block chains need fully validating stores. In
the lightweight SPV mode, a SPVBlockStore
is the right choice.
This class implements an abstract class which makes it simple to create a BlockChain that does/doesn't do full verification. It verifies headers and is implements most of what is required to implement SPV mode, but also provides callback hooks which can be used to do full verification.
There are two subclasses of AbstractBlockChain that are useful: BlockChain
, which is the simplest
class and implements simplified payment verification. This is a lightweight and efficient mode that does
not verify the contents of blocks, just their headers. A FullPrunedBlockChain
paired with a
H2FullPrunedBlockStore
implements full verification, which is equivalent to
Bitcoin Core. To learn more about the alternative security models, please consult the articles on the
website.
The 'chain' is actually a tree although in normal operation it operates mostly as a list of Block
s.
When multiple new head blocks are found simultaneously, there are multiple stories of the economy competing to become
the one true consensus. This can happen naturally when two miners solve a block within a few seconds of each other,
or it can happen when the chain is under attack.
A reference to the head block of the best known chain is stored. If you can reach the genesis block by repeatedly walking through the prevBlock pointers, then we say this is a full chain. If you cannot reach the genesis block we say it is an orphan chain. Orphan chains can occur when blocks are solved and received during the initial block chain download, or if we connect to a peer that doesn't send us blocks in order.
A reorganize occurs when the blocks that make up the best known chain change. Note that simply adding a new block to the top of the best chain isn't a reorganize, but that a reorganize is always triggered by adding a new block that connects to some other (non best head) block. By "best" we mean the chain representing the largest amount of work done.
Every so often the block chain passes a difficulty transition point. At that time, all the blocks in the last 2016 blocks are examined and a new difficulty target is calculated from them.
Modifier and Type | Class and Description |
---|---|
static class |
AbstractBlockChain.NewBlockType
Indicates whether new Block was on the best chain or not
|
Modifier and Type | Field and Description |
---|---|
protected StoredBlock |
chainHead
Tracks the top of the best known chain.
|
static double |
FP_ESTIMATOR_ALPHA
False positive estimation uses a double exponential moving average.
|
static double |
FP_ESTIMATOR_BETA
False positive estimation uses a double exponential moving average.
|
protected java.util.concurrent.locks.ReentrantLock |
lock
synchronization lock
|
protected NetworkParameters |
params
network parameters for this chain
|
Constructor and Description |
---|
AbstractBlockChain(Context context,
java.util.List<? extends Wallet> wallets,
BlockStore blockStore)
Constructs a BlockChain connected to the given list of listeners (wallets) and a store.
|
AbstractBlockChain(NetworkParameters params,
java.util.List<? extends Wallet> wallets,
BlockStore blockStore)
Constructs a BlockChain connected to the given list of listeners (wallets) and a store.
|
Modifier and Type | Method and Description |
---|---|
boolean |
add(Block block)
Processes a received block and tries to add it to the chain.
|
boolean |
add(FilteredBlock block)
Processes a received block and tries to add it to the chain.
|
void |
addNewBestBlockListener(java.util.concurrent.Executor executor,
NewBestBlockListener listener)
Adds a
NewBestBlockListener listener to the chain. |
void |
addNewBestBlockListener(NewBestBlockListener listener)
Adds a
NewBestBlockListener listener to the chain. |
void |
addReorganizeListener(java.util.concurrent.Executor executor,
ReorganizeListener listener)
Adds a generic
ReorganizeListener listener to the chain. |
void |
addReorganizeListener(ReorganizeListener listener)
Adds a generic
ReorganizeListener listener to the chain. |
protected abstract StoredBlock |
addToBlockStore(StoredBlock storedPrev,
Block block)
Adds/updates the given
Block with the block store. |
protected abstract StoredBlock |
addToBlockStore(StoredBlock storedPrev,
Block header,
TransactionOutputChanges txOutputChanges)
Adds/updates the given
StoredBlock with the block store. |
void |
addTransactionReceivedListener(java.util.concurrent.Executor executor,
TransactionReceivedInBlockListener listener)
Adds a generic
TransactionReceivedInBlockListener listener to the chain. |
void |
addTransactionReceivedListener(TransactionReceivedInBlockListener listener)
Adds a generic
TransactionReceivedInBlockListener listener to the chain. |
void |
addWallet(Wallet wallet)
Add a wallet to the BlockChain.
|
protected abstract TransactionOutputChanges |
connectTransactions(int height,
Block block)
Connect each transaction in block.transactions, verifying them as we go and removing spent outputs
If an error is encountered in a transaction, no changes should be made to the underlying BlockStore.
|
protected abstract TransactionOutputChanges |
connectTransactions(StoredBlock newBlock)
Load newBlock from BlockStore and connect its transactions, returning changes to the set of unspent transactions.
|
protected abstract void |
disconnectTransactions(StoredBlock block)
Disconnect each transaction in the block (after reading it from the block store)
Only called if(shouldVerifyTransactions())
|
protected abstract void |
doSetChainHead(StoredBlock chainHead)
Called before setting chain head in memory.
|
java.util.Set<Sha256Hash> |
drainOrphanBlocks()
Returns the hashes of the currently stored orphan blocks and then deletes them from this objects storage.
|
java.util.Date |
estimateBlockTime(int height)
Returns an estimate of when the given block will be reached, assuming a perfect 10 minute average for each
block.
|
int |
getBestChainHeight() |
BlockStore |
getBlockStore()
Returns the
BlockStore the chain was constructed with. |
StoredBlock |
getChainHead()
Returns the block at the head of the current best chain.
|
double |
getFalsePositiveRate()
The false positive rate is the average over all blockchain transactions of:
- 1.0 if the transaction was false-positive (was irrelevant to all listeners)
- 0.0 if the transaction was relevant or filtered out
|
com.google.common.util.concurrent.ListenableFuture<StoredBlock> |
getHeightFuture(int height)
Returns a future that completes when the block chain has reached the given height.
|
Block |
getOrphanRoot(Sha256Hash from)
An orphan block is one that does not connect to the chain anywhere (ie we can't find its parent, therefore
it's an orphan).
|
protected abstract StoredBlock |
getStoredBlockInCurrentScope(Sha256Hash hash)
For a standard BlockChain, this should return blockStore.get(hash),
for a FullPrunedBlockChain blockStore.getOnceUndoableStoredBlock(hash)
|
protected VersionTally |
getVersionTally() |
boolean |
isOrphan(Sha256Hash block)
Returns true if the given block is currently in the orphan blocks list.
|
protected abstract void |
notSettingChainHead()
Called if we (possibly) previously called disconnectTransaction/connectTransactions,
but will not be calling preSetChainHead as a block failed verification.
|
void |
removeNewBestBlockListener(NewBestBlockListener listener)
Removes the given
NewBestBlockListener from the chain. |
void |
removeReorganizeListener(ReorganizeListener listener)
Removes the given
ReorganizeListener from the chain. |
void |
removeTransactionReceivedListener(TransactionReceivedInBlockListener listener)
Removes the given
TransactionReceivedInBlockListener from the chain. |
void |
removeWallet(Wallet wallet)
Remove a wallet from the chain.
|
void |
resetFalsePositiveEstimate()
Resets estimates of false positives.
|
protected abstract void |
rollbackBlockStore(int height)
Rollback the block store to a given height.
|
protected void |
setChainHead(StoredBlock chainHead) |
protected abstract boolean |
shouldVerifyTransactions()
Whether or not we are maintaining a set of unspent outputs and are verifying all transactions.
|
protected void |
trackFilteredTransactions(int count)
We completed handling of a filtered block.
|
protected final java.util.concurrent.locks.ReentrantLock lock
protected StoredBlock chainHead
Following this one down to the genesis block produces the story of the economy from the creation of Bitcoin until the present day. The chain head can change if a new set of blocks is received that results in a chain of greater work than the one obtained by following this one down. In that case a reorganize is triggered, potentially invalidating transactions in our wallet.
protected final NetworkParameters params
public static final double FP_ESTIMATOR_ALPHA
public static final double FP_ESTIMATOR_BETA
public AbstractBlockChain(NetworkParameters params, java.util.List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException
params
- network parameters for this chainwallets
- list of listeners (wallets)blockStore
- where to store blocksBlockStoreException
- if a failure occurs while storing a blockpublic AbstractBlockChain(Context context, java.util.List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException
context
- context for this networkwallets
- list of listeners (wallets)blockStore
- where to store blocksBlockStoreException
- if a failure occurs while storing a blockpublic final void addWallet(Wallet wallet)
wallet
- wallet to addpublic void removeWallet(Wallet wallet)
wallet
- wallet to removepublic void addNewBestBlockListener(NewBestBlockListener listener)
NewBestBlockListener
listener to the chain.listener
- listener to addpublic final void addNewBestBlockListener(java.util.concurrent.Executor executor, NewBestBlockListener listener)
NewBestBlockListener
listener to the chain.executor
- executor to listen onlistener
- listener to addpublic void addReorganizeListener(ReorganizeListener listener)
ReorganizeListener
listener to the chain.listener
- listener to addpublic final void addReorganizeListener(java.util.concurrent.Executor executor, ReorganizeListener listener)
ReorganizeListener
listener to the chain.executor
- executor to listen onlistener
- listener to addpublic void addTransactionReceivedListener(TransactionReceivedInBlockListener listener)
TransactionReceivedInBlockListener
listener to the chain.listener
- listener to addpublic final void addTransactionReceivedListener(java.util.concurrent.Executor executor, TransactionReceivedInBlockListener listener)
TransactionReceivedInBlockListener
listener to the chain.executor
- executor to listen onlistener
- listener to addpublic void removeNewBestBlockListener(NewBestBlockListener listener)
NewBestBlockListener
from the chain.listener
- listener to removepublic void removeReorganizeListener(ReorganizeListener listener)
ReorganizeListener
from the chain.listener
- listener to removepublic void removeTransactionReceivedListener(TransactionReceivedInBlockListener listener)
TransactionReceivedInBlockListener
from the chain.listener
- listener to removepublic BlockStore getBlockStore()
BlockStore
the chain was constructed with. You can use this to iterate over the chain.BlockStore
the chain was constructed withprotected abstract StoredBlock addToBlockStore(StoredBlock storedPrev, Block block) throws BlockStoreException, VerificationException
Block
with the block store.
This version is used when the transactions have not been verified.storedPrev
- The StoredBlock
which immediately precedes block.block
- The Block
to add/update.StoredBlock
BlockStoreException
- if a failure occurs while storing a blockVerificationException
- if the block is invalidprotected abstract StoredBlock addToBlockStore(StoredBlock storedPrev, Block header, @Nullable TransactionOutputChanges txOutputChanges) throws BlockStoreException, VerificationException
StoredBlock
with the block store.
This version is used when the transactions have already been verified to properly spend txOutputChanges.storedPrev
- The StoredBlock
which immediately precedes block.header
- The StoredBlock
to add/update.txOutputChanges
- The total sum of all changes made by this block to the set of open transaction outputs
(from a call to connectTransactions), if in fully verifying mode (null otherwise).StoredBlock
BlockStoreException
- if a failure occurs while storing a blockVerificationException
- if the block is invalidprotected abstract void rollbackBlockStore(int height) throws BlockStoreException
BlockChain
instances.height
- height to roll back toBlockStoreException
- if the operation fails or is unsupported.protected abstract void doSetChainHead(StoredBlock chainHead) throws BlockStoreException
chainHead
- chain head to setBlockStoreException
- if a failure occurs while storing a blockprotected abstract void notSettingChainHead() throws BlockStoreException
BlockStoreException
- if a failure occurs while storing a blockprotected abstract StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException
hash
- hash of block to fetchBlockStoreException
- if a failure occurs while storing a blockpublic boolean add(Block block) throws VerificationException, PrunedException
block
- block to addVerificationException
- block is invalid or contains invalid transactionsPrunedException
- a reorg that is too-long for our stored block data has occurredpublic boolean add(FilteredBlock block) throws VerificationException, PrunedException
block
- received blockVerificationException
- if invalid blockPrunedException
- a reorg that is too-long for our stored block data has occurredprotected abstract boolean shouldVerifyTransactions()
protected abstract TransactionOutputChanges connectTransactions(int height, Block block) throws VerificationException, BlockStoreException
height
- block height to attach atblock
- block to connectVerificationException
- if an attempt was made to spend an already-spent output, or if a transaction incorrectly solved an output script.BlockStoreException
- if the block store had an underlying error.protected abstract TransactionOutputChanges connectTransactions(StoredBlock newBlock) throws VerificationException, BlockStoreException, PrunedException
newBlock
- block to loadPrunedException
- if newBlock does not exist as a StoredUndoableBlock
in the block store.VerificationException
- if an attempt was made to spend an already-spent output, or if a transaction incorrectly solved an output script.BlockStoreException
- if the block store had an underlying error or newBlock does not exist in the block store at all.public java.util.Set<Sha256Hash> drainOrphanBlocks()
protected abstract void disconnectTransactions(StoredBlock block) throws PrunedException, BlockStoreException
block
- block to disconnectPrunedException
- if block does not exist as a StoredUndoableBlock
in the block store.BlockStoreException
- if the block store had an underlying error or block does not exist in the block store at all.public final int getBestChainHeight()
getChainHead().getHeight()
.protected void setChainHead(StoredBlock chainHead) throws BlockStoreException
chainHead
- chain head to setBlockStoreException
- if a failure occurs while storing a blockpublic StoredBlock getChainHead()
@Nullable public Block getOrphanRoot(Sha256Hash from)
from
- hash of block to walk backwards frompublic boolean isOrphan(Sha256Hash block)
block
- block to checkpublic java.util.Date estimateBlockTime(int height)
height
- block time to estimatepublic com.google.common.util.concurrent.ListenableFuture<StoredBlock> getHeightFuture(int height)
StoredBlock
of the block that reaches that height first. The future completes on a peer thread.height
- desired heightpublic double getFalsePositiveRate()
protected void trackFilteredTransactions(int count)
count
- total number of transactions in original blockpublic void resetFalsePositiveEstimate()
protected VersionTally getVersionTally()