From 1a70f05ca7cc5ed1f49512f1f49c358ffb129e83 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Sun, 1 Mar 2015 21:23:46 +0100 Subject: [PATCH] Add a notion of an event horizon to the context. This is intended to unify the various places in the library where we want to throw data away after a tx is buried so deep it will probably never be re-orged, but which presently use different constants. The wallet will use the event horizon in future as well. --- .../main/java/org/bitcoinj/core/Context.java | 24 +++++++++++++++++++ .../main/java/org/bitcoinj/core/Wallet.java | 2 +- .../channels/PaymentChannelClientState.java | 6 ++--- .../channels/ChannelConnectionTest.java | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Context.java b/core/src/main/java/org/bitcoinj/core/Context.java index 8775c023..4e91d951 100644 --- a/core/src/main/java/org/bitcoinj/core/Context.java +++ b/core/src/main/java/org/bitcoinj/core/Context.java @@ -16,10 +16,13 @@ public class Context { private TxConfidenceTable confidenceTable; private NetworkParameters params; + private int eventHorizon = 100; /** * Creates a new context object. For now, this will be done for you by the framework. Eventually you will be * expected to do this yourself in the same manner as fetching a NetworkParameters object (at the start of your app). + * + * @param params The network parameters that will be associated with this context. */ public Context(NetworkParameters params) { this.confidenceTable = new TxConfidenceTable(); @@ -29,6 +32,18 @@ public class Context { slot.set(this); } + /** + * Creates a new context object. For now, this will be done for you by the framework. Eventually you will be + * expected to do this yourself in the same manner as fetching a NetworkParameters object (at the start of your app). + * + * @param params The network parameters that will be associated with this context. + * @param eventHorizon Number of blocks after which the library will delete data and be unable to always process reorgs (see {@link #getEventHorizon()}. + */ + public Context(NetworkParameters params, int eventHorizon) { + this(params); + this.eventHorizon = eventHorizon; + } + private static volatile Context lastConstructed; private static final ThreadLocal slot = new ThreadLocal(); @@ -106,4 +121,13 @@ public class Context { public NetworkParameters getParams() { return params; } + + /** + * The event horizon is the number of blocks after which various bits of the library consider a transaction to be + * so confirmed that it's safe to delete data. Re-orgs larger than the event horizon will not be correctly + * processed, so the default value is high (100). + */ + public int getEventHorizon() { + return eventHorizon; + } } diff --git a/core/src/main/java/org/bitcoinj/core/Wallet.java b/core/src/main/java/org/bitcoinj/core/Wallet.java index e24bfc69..b7485f1e 100644 --- a/core/src/main/java/org/bitcoinj/core/Wallet.java +++ b/core/src/main/java/org/bitcoinj/core/Wallet.java @@ -1914,7 +1914,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha // included once again. We could have a separate was-in-chain-and-now-isn't confidence type // but this way is backwards compatible with existing software, and the new state probably // wouldn't mean anything different to just remembering peers anyway. - if (confidence.incrementDepthInBlocks() > 100) + if (confidence.incrementDepthInBlocks() > context.getEventHorizon()) confidence.clearBroadcastBy(); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } diff --git a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java index 4dd02eef..e394a083 100644 --- a/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java +++ b/core/src/main/java/org/bitcoinj/protocols/channels/PaymentChannelClientState.java @@ -69,7 +69,6 @@ import static com.google.common.base.Preconditions.*; */ public class PaymentChannelClientState { private static final Logger log = LoggerFactory.getLogger(PaymentChannelClientState.class); - private static final int CONFIRMATIONS_FOR_DELETE = 3; private final Wallet wallet; // Both sides need a key (private in our case, public for the server) in order to manage the multisig contract @@ -193,11 +192,12 @@ public class PaymentChannelClientState { } private void watchCloseConfirmations() { - // When we see the close transaction get a few confirmations, we can just delete the record + // When we see the close transaction get enough confirmations, we can just delete the record // of this channel along with the refund tx from the wallet, because we're not going to need // any of that any more. final TransactionConfidence confidence = storedChannel.close.getConfidence(); - ListenableFuture future = confidence.getDepthFuture(CONFIRMATIONS_FOR_DELETE, Threading.SAME_THREAD); + int numConfirms = Context.get().getEventHorizon(); + ListenableFuture future = confidence.getDepthFuture(numConfirms, Threading.SAME_THREAD); Futures.addCallback(future, new FutureCallback() { @Override public void onSuccess(TransactionConfidence result) { diff --git a/core/src/test/java/org/bitcoinj/protocols/channels/ChannelConnectionTest.java b/core/src/test/java/org/bitcoinj/protocols/channels/ChannelConnectionTest.java index a391a658..fc88e4b5 100644 --- a/core/src/test/java/org/bitcoinj/protocols/channels/ChannelConnectionTest.java +++ b/core/src/test/java/org/bitcoinj/protocols/channels/ChannelConnectionTest.java @@ -71,7 +71,8 @@ public class ChannelConnectionTest extends TestWithWallet { sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN); wallet.addExtension(new StoredPaymentChannelClientStates(wallet, failBroadcaster)); - serverWallet = new Wallet(params); + Context context = new Context(params, 3); // Shorter event horizon for unit tests. + serverWallet = new Wallet(context); serverWallet.addExtension(new StoredPaymentChannelServerStates(serverWallet, failBroadcaster)); serverWallet.freshReceiveKey(); // Use an atomic boolean to indicate failure because fail()/assert*() dont work in network threads