From aae60d839144fa8130271cfdf6074213c487e504 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Sun, 1 Mar 2015 21:12:00 +0100 Subject: [PATCH] Add NetworkParameters to the Context, and verify consistency when a context is fetched. Make Wallet accept Context as a c'tor parameter and then update unit tests (which switch params very often) to create contexts when necessary. --- .../java/org/bitcoinj/core/BlockChain.java | 2 +- .../main/java/org/bitcoinj/core/Context.java | 28 +++++++++++++---- .../java/org/bitcoinj/core/PeerGroup.java | 2 +- .../bitcoinj/core/TransactionConfidence.java | 2 ++ .../main/java/org/bitcoinj/core/Wallet.java | 19 ++++++++++-- .../testing/TestWithNetworkConnections.java | 2 +- .../AbstractFullPrunedBlockChainTest.java | 5 ++-- .../org/bitcoinj/core/BlockChainTest.java | 6 ++-- .../org/bitcoinj/core/BloomFilterTest.java | 3 +- .../org/bitcoinj/core/ChainSplitTest.java | 4 ++- .../org/bitcoinj/core/CoinbaseBlockTest.java | 4 +-- ...ilteredBlockAndPartialMerkleTreeTests.java | 3 ++ .../bitcoinj/core/LazyParseByteCacheTest.java | 8 +++-- .../bitcoinj/core/TxConfidenceTableTest.java | 2 +- .../wallet/DefaultRiskAnalysisTest.java | 30 ++++++++----------- 15 files changed, 78 insertions(+), 42 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/BlockChain.java b/core/src/main/java/org/bitcoinj/core/BlockChain.java index 129ecfb5..00df286e 100644 --- a/core/src/main/java/org/bitcoinj/core/BlockChain.java +++ b/core/src/main/java/org/bitcoinj/core/BlockChain.java @@ -46,7 +46,7 @@ public class BlockChain extends AbstractBlockChain { */ public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException { this(params, new ArrayList(), blockStore); - Context.getOrCreate(); + Context.getOrCreate(params); if (wallet != null) addWallet(wallet); } diff --git a/core/src/main/java/org/bitcoinj/core/Context.java b/core/src/main/java/org/bitcoinj/core/Context.java index 498b93a1..8775c023 100644 --- a/core/src/main/java/org/bitcoinj/core/Context.java +++ b/core/src/main/java/org/bitcoinj/core/Context.java @@ -13,14 +13,17 @@ import static com.google.common.base.Preconditions.checkState; */ public class Context { private static final Logger log = LoggerFactory.getLogger(Context.class); + private TxConfidenceTable confidenceTable; + private NetworkParameters params; /** * 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). */ - public Context() { - confidenceTable = new TxConfidenceTable(); + public Context(NetworkParameters params) { + this.confidenceTable = new TxConfidenceTable(); + this.params = params; lastConstructed = this; // We may already have a context in our TLS slot. This can happen a lot during unit tests, so just ignore it. slot.set(this); @@ -58,14 +61,18 @@ public class Context { } // A temporary internal shim designed to help us migrate internally in a way that doesn't wreck source compatibility. - static Context getOrCreate() { + static Context getOrCreate(NetworkParameters params) { + Context context; try { - return get(); + context = get(); } catch (IllegalStateException e) { log.warn("Implicitly creating context. This is a migration step and this message will eventually go away."); - new Context(); - return slot.get(); + context = new Context(params); + return context; } + if (context.getParams() != params) + throw new IllegalStateException("Context does not match implicit network params: " + context.getParams() + " vs " + params); + return context; } /** @@ -90,4 +97,13 @@ public class Context { public TxConfidenceTable getConfidenceTable() { return confidenceTable; } + + /** + * Returns the {@link org.bitcoinj.core.NetworkParameters} specified when this context was (auto) created. The + * network parameters defines various hard coded constants for a specific instance of a Bitcoin network, such as + * main net, testnet, etc. + */ + public NetworkParameters getParams() { + return params; + } } diff --git a/core/src/main/java/org/bitcoinj/core/PeerGroup.java b/core/src/main/java/org/bitcoinj/core/PeerGroup.java index 1e27cc28..4e16e725 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerGroup.java +++ b/core/src/main/java/org/bitcoinj/core/PeerGroup.java @@ -309,7 +309,7 @@ public class PeerGroup implements TransactionBroadcaster { */ private PeerGroup(NetworkParameters params, @Nullable AbstractBlockChain chain, ClientConnectionManager connectionManager, @Nullable TorClient torClient) { this.params = checkNotNull(params); - this.context = Context.getOrCreate(); + this.context = Context.getOrCreate(params); this.chain = chain; fastCatchupTimeSecs = params.getGenesisBlock().getTimeSeconds(); wallets = new CopyOnWriteArrayList(); diff --git a/core/src/main/java/org/bitcoinj/core/TransactionConfidence.java b/core/src/main/java/org/bitcoinj/core/TransactionConfidence.java index 0844ff3d..b369783f 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionConfidence.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionConfidence.java @@ -28,6 +28,8 @@ import java.util.concurrent.*; import static com.google.common.base.Preconditions.*; +// TODO: Modify the getDepthInBlocks method to require the chain height to be specified, in preparation for ceasing to touch every tx on every block. + /** *

A TransactionConfidence object tracks data you can use to make a confidence decision about a transaction. * It also contains some pre-canned rules for common scenarios: if you aren't really sure what level of confidence diff --git a/core/src/main/java/org/bitcoinj/core/Wallet.java b/core/src/main/java/org/bitcoinj/core/Wallet.java index 0bb6ef63..e24bfc69 100644 --- a/core/src/main/java/org/bitcoinj/core/Wallet.java +++ b/core/src/main/java/org/bitcoinj/core/Wallet.java @@ -212,6 +212,15 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha this(params, new KeyChainGroup(params)); } + /** + * Creates a new, empty wallet with a randomly chosen seed and no transactions. Make sure to provide for sufficient + * backup! Any keys will be derived from the seed. If you want to restore a wallet from disk instead, see + * {@link #loadFromFile}. + */ + public Wallet(Context context) { + this(context, new KeyChainGroup(context.getParams())); + } + public static Wallet fromSeed(NetworkParameters params, DeterministicSeed seed) { return new Wallet(params, new KeyChainGroup(params, seed)); } @@ -244,11 +253,15 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha return new Wallet(params, group); } + public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) { + this(Context.getOrCreate(params), keyChainGroup); + } + // TODO: When this class moves to the Wallet package, along with the protobuf serializer, then hide this. /** For internal use only. */ - public Wallet(NetworkParameters params, KeyChainGroup keyChainGroup) { - this.context = Context.getOrCreate(); - this.params = checkNotNull(params); + public Wallet(Context context, KeyChainGroup keyChainGroup) { + this.context = context; + this.params = context.getParams(); this.keychain = checkNotNull(keyChainGroup); if (params == UnitTestParams.get()) this.keychain.setLookaheadSize(5); // Cut down excess computation for unit tests. diff --git a/core/src/main/java/org/bitcoinj/testing/TestWithNetworkConnections.java b/core/src/main/java/org/bitcoinj/testing/TestWithNetworkConnections.java index 0f598b31..4fcb2a14 100644 --- a/core/src/main/java/org/bitcoinj/testing/TestWithNetworkConnections.java +++ b/core/src/main/java/org/bitcoinj/testing/TestWithNetworkConnections.java @@ -81,7 +81,7 @@ public class TestWithNetworkConnections { BriefLogFormatter.init(); unitTestParams = UnitTestParams.get(); - new Context(); + new Context(unitTestParams); Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO; this.blockStore = blockStore; // Allow subclasses to override the wallet object with their own. diff --git a/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java b/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java index a88fff39..32e8eaf4 100644 --- a/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java +++ b/core/src/test/java/org/bitcoinj/core/AbstractFullPrunedBlockChainTest.java @@ -43,13 +43,13 @@ import static org.junit.Assert.*; * We don't do any wallet tests here, we leave that to {@link ChainSplitTest} */ -public abstract class AbstractFullPrunedBlockChainTest -{ +public abstract class AbstractFullPrunedBlockChainTest { private static final Logger log = LoggerFactory.getLogger(AbstractFullPrunedBlockChainTest.class); protected NetworkParameters params; protected FullPrunedBlockChain chain; protected FullPrunedBlockStore store; + protected Context context; @Before public void setUp() throws Exception { @@ -59,6 +59,7 @@ public abstract class AbstractFullPrunedBlockChainTest return 10000; } }; + context = new Context(params); } public abstract FullPrunedBlockStore createStore(NetworkParameters params, int blockCount) diff --git a/core/src/test/java/org/bitcoinj/core/BlockChainTest.java b/core/src/test/java/org/bitcoinj/core/BlockChainTest.java index 49143ea9..e3834a31 100644 --- a/core/src/test/java/org/bitcoinj/core/BlockChainTest.java +++ b/core/src/test/java/org/bitcoinj/core/BlockChainTest.java @@ -67,11 +67,13 @@ public class BlockChainTest { @Before public void setUp() throws Exception { BriefLogFormatter.initVerbose(); - testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet)); + Context testNetContext = new Context(testNet); + testNetChain = new BlockChain(testNet, new Wallet(testNetContext), new MemoryBlockStore(testNet)); Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO; unitTestParams = UnitTestParams.get(); - wallet = new Wallet(unitTestParams) { + Context context = new Context(unitTestParams); + wallet = new Wallet(context) { @Override public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException { diff --git a/core/src/test/java/org/bitcoinj/core/BloomFilterTest.java b/core/src/test/java/org/bitcoinj/core/BloomFilterTest.java index 440bb79f..790e5176 100644 --- a/core/src/test/java/org/bitcoinj/core/BloomFilterTest.java +++ b/core/src/test/java/org/bitcoinj/core/BloomFilterTest.java @@ -68,6 +68,7 @@ public class BloomFilterTest { @Test public void walletTest() throws Exception { NetworkParameters params = MainNetParams.get(); + Context context = new Context(params); DumpedPrivateKey privKey = new DumpedPrivateKey(params, "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"); @@ -77,7 +78,7 @@ public class BloomFilterTest { KeyChainGroup group = new KeyChainGroup(params); // Add a random key which happens to have been used in a recent generation group.importKeys(privKey.getKey(), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99"))); - Wallet wallet = new Wallet(params, group); + Wallet wallet = new Wallet(context, group); wallet.commitTx(new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038754030114062f503253482fffffffff01c05e559500000000232103cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99ac00000000"))); // We should have 2 per pubkey, and one for the pay-2-pubkey output we have diff --git a/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java b/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java index b2707cb8..5d87e7cf 100644 --- a/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java +++ b/core/src/test/java/org/bitcoinj/core/ChainSplitTest.java @@ -51,6 +51,7 @@ public class ChainSplitTest { private Address coinsTo2; private Address someOtherGuy; private MemoryBlockStore blockStore; + private Context context; @Before public void setUp() throws Exception { @@ -58,8 +59,9 @@ public class ChainSplitTest { Utils.setMockClock(); // Use mock clock Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO; unitTestParams = UnitTestParams.get(); + context = new Context(unitTestParams); blockStore = new MemoryBlockStore(unitTestParams); - wallet = new Wallet(unitTestParams); + wallet = new Wallet(context); ECKey key1 = wallet.freshReceiveKey(); ECKey key2 = wallet.freshReceiveKey(); chain = new BlockChain(unitTestParams, wallet, blockStore); diff --git a/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java b/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java index 6a9e8e34..fa6fc368 100644 --- a/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java +++ b/core/src/test/java/org/bitcoinj/core/CoinbaseBlockTest.java @@ -65,8 +65,8 @@ public class CoinbaseBlockTest { // Create a wallet contain the miner's key that receives a spend from a coinbase. ECKey miningKey = (new DumpedPrivateKey(params, MINING_PRIVATE_KEY)).getKey(); assertNotNull(miningKey); - - Wallet wallet = new Wallet(params); + Context context = new Context(params); + Wallet wallet = new Wallet(context); wallet.importKey(miningKey); // Initial balance should be zero by construction. diff --git a/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java b/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java index ede8c1bb..218cffc5 100644 --- a/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java +++ b/core/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTests.java @@ -41,6 +41,8 @@ import static org.junit.Assert.assertTrue; @RunWith(value = Parameterized.class) public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup { + private Context context; + @Parameterized.Parameters public static Collection parameters() { return Arrays.asList(new ClientType[] {ClientType.NIO_CLIENT_MANAGER}, @@ -112,6 +114,7 @@ public class FilteredBlockAndPartialMerkleTreeTests extends TestWithPeerGroup { @Test public void serializeDownloadBlockWithWallet() throws Exception { unitTestParams = UnitTestParams.get(); + context = new Context(unitTestParams); // First we create all the neccessary objects, including lots of serialization and double-checks // Note that all serialized forms here are generated by the reference client/pulled from block explorer diff --git a/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java b/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java index f4b010bf..bf5e9553 100644 --- a/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java +++ b/core/src/test/java/org/bitcoinj/core/LazyParseByteCacheTest.java @@ -61,7 +61,8 @@ public class LazyParseByteCacheTest { "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a"); - + + private Context context; private Wallet wallet; private BlockStore blockStore; private NetworkParameters unitTestParams; @@ -74,7 +75,7 @@ public class LazyParseByteCacheTest { private byte[] tx2Bytes; private byte[] tx2BytesWithHeader; - + private void resetBlockStore() { blockStore = new MemoryBlockStore(unitTestParams); } @@ -82,7 +83,8 @@ public class LazyParseByteCacheTest { @Before public void setUp() throws Exception { unitTestParams = UnitTestParams.get(); - wallet = new Wallet(unitTestParams); + context = new Context(unitTestParams); + wallet = new Wallet(context); wallet.freshReceiveKey(); resetBlockStore(); diff --git a/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java b/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java index b8aab869..b73ed4a5 100644 --- a/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java +++ b/core/src/test/java/org/bitcoinj/core/TxConfidenceTableTest.java @@ -35,7 +35,7 @@ public class TxConfidenceTableTest { @Before public void setup() throws Exception { BriefLogFormatter.init(); - Context context = new Context(); + Context context = new Context(params); table = context.getConfidenceTable(); Address to = new ECKey().toAddress(params); diff --git a/core/src/test/java/org/bitcoinj/wallet/DefaultRiskAnalysisTest.java b/core/src/test/java/org/bitcoinj/wallet/DefaultRiskAnalysisTest.java index e7eeb29a..80bacb7b 100644 --- a/core/src/test/java/org/bitcoinj/wallet/DefaultRiskAnalysisTest.java +++ b/core/src/test/java/org/bitcoinj/wallet/DefaultRiskAnalysisTest.java @@ -17,25 +17,19 @@ package org.bitcoinj.wallet; -import java.util.Arrays; +import com.google.common.collect.*; import org.bitcoinj.core.*; -import org.bitcoinj.crypto.TransactionSignature; -import org.bitcoinj.params.MainNetParams; -import org.bitcoinj.script.Script; -import org.bitcoinj.script.ScriptBuilder; -import org.bitcoinj.script.ScriptChunk; -import com.google.common.collect.ImmutableList; -import org.bitcoinj.wallet.DefaultRiskAnalysis; -import org.bitcoinj.wallet.RiskAnalysis; -import org.bitcoinj.wallet.DefaultRiskAnalysis.RuleViolation; -import org.junit.Before; -import org.junit.Test; +import org.bitcoinj.crypto.*; +import org.bitcoinj.params.*; +import org.bitcoinj.script.*; +import org.bitcoinj.wallet.DefaultRiskAnalysis.*; +import org.junit.*; -import static org.bitcoinj.core.Coin.COIN; -import static org.bitcoinj.script.ScriptOpCodes.OP_PUSHDATA1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import java.util.*; + +import static org.bitcoinj.core.Coin.*; +import static org.bitcoinj.script.ScriptOpCodes.*; +import static org.junit.Assert.*; public class DefaultRiskAnalysisTest { // Uses mainnet because isStandard checks are disabled on testnet. @@ -47,7 +41,7 @@ public class DefaultRiskAnalysisTest { @Before public void setup() { - wallet = new Wallet(params) { + wallet = new Wallet(new Context(params)) { @Override public int getLastBlockSeenHeight() { return 1000;