diff --git a/base/src/main/java/org/bitcoinj/base/utils/Fiat.java b/base/src/main/java/org/bitcoinj/base/utils/Fiat.java index ea61ac985d8..eafcaa11f2f 100644 --- a/base/src/main/java/org/bitcoinj/base/utils/Fiat.java +++ b/base/src/main/java/org/bitcoinj/base/utils/Fiat.java @@ -46,7 +46,7 @@ public final class Fiat implements Monetary, Comparable { private Fiat(final String currencyCode, final long value) { this.value = value; - this.currencyCode = currencyCode; + this.currencyCode = Objects.requireNonNull(currencyCode); } public static Fiat valueOf(final String currencyCode, final long value) { diff --git a/core/src/main/java/org/bitcoinj/store/BlockStore.java b/core/src/main/java/org/bitcoinj/store/BlockStore.java index 5a815eaa13f..5581d9a26b4 100644 --- a/core/src/main/java/org/bitcoinj/store/BlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/BlockStore.java @@ -19,6 +19,7 @@ import org.bitcoinj.core.BlockChain; import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.core.StoredBlock; +import org.jspecify.annotations.Nullable; /** * An implementor of BlockStore saves StoredBlock objects to disk. Different implementations store them in @@ -42,6 +43,7 @@ public interface BlockStore { * Returns the StoredBlock given a hash. The returned values block.getHash() method will be equal to the * parameter. If no such block is found, returns null. */ + @Nullable StoredBlock get(Sha256Hash hash) throws BlockStoreException; /** @@ -56,7 +58,7 @@ public interface BlockStore { * Sets the {@link StoredBlock} that represents the top of the chain of greatest total work. */ void setChainHead(StoredBlock chainHead) throws BlockStoreException; - + /** Closes the store. */ void close() throws BlockStoreException; } diff --git a/core/src/main/java/org/bitcoinj/store/FullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/FullPrunedBlockStore.java index 65e7092702b..3188bbc40e9 100644 --- a/core/src/main/java/org/bitcoinj/store/FullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/FullPrunedBlockStore.java @@ -21,6 +21,7 @@ import org.bitcoinj.core.StoredUndoableBlock; import org.bitcoinj.core.UTXO; import org.bitcoinj.core.UTXOProvider; +import org.jspecify.annotations.Nullable; /** @@ -66,6 +67,7 @@ public interface FullPrunedBlockStore extends BlockStore, UTXOProvider { * Returns the StoredBlock that was added as a StoredUndoableBlock given a hash. The returned values block.getHash() * method will be equal to the parameter. If no such block is found, returns null. */ + @Nullable StoredBlock getOnceUndoableStoredBlock(Sha256Hash hash) throws BlockStoreException; /** @@ -73,11 +75,13 @@ public interface FullPrunedBlockStore extends BlockStore, UTXOProvider { * block is found, returns null. Note that this may return null more often than get(Sha256Hash hash) as not all * {@link StoredBlock}s have a {@link StoredUndoableBlock} copy stored as well. */ + @Nullable StoredUndoableBlock getUndoBlock(Sha256Hash hash) throws BlockStoreException; /** * Gets a {@link UTXO} with the given hash and index, or null if none is found */ + @Nullable UTXO getTransactionOutput(Sha256Hash hash, long index) throws BlockStoreException; /** @@ -103,6 +107,7 @@ public interface FullPrunedBlockStore extends BlockStore, UTXOProvider { * been fully verified and the point in the chain at which the unspent transaction output set in this * store represents. */ + @Nullable StoredBlock getVerifiedChainHead() throws BlockStoreException; /** diff --git a/core/src/main/java/org/bitcoinj/store/MemoryBlockStore.java b/core/src/main/java/org/bitcoinj/store/MemoryBlockStore.java index 26eb7b7e2ee..516859f64e2 100644 --- a/core/src/main/java/org/bitcoinj/store/MemoryBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/MemoryBlockStore.java @@ -20,6 +20,7 @@ import org.bitcoinj.base.Sha256Hash; import org.bitcoinj.core.StoredBlock; import org.bitcoinj.core.VerificationException; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.Map; @@ -28,10 +29,10 @@ * Keeps {@link StoredBlock}s in memory. Used primarily for unit testing. */ public class MemoryBlockStore implements BlockStore { - private LinkedHashMap blockMap = new LinkedHashMap() { + private @Nullable LinkedHashMap blockMap = new LinkedHashMap() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { - return blockMap.size() > 5000; + return this.size() > 5000; } }; private StoredBlock chainHead; @@ -41,7 +42,7 @@ public MemoryBlockStore(Block genesisBlock) { Block genesisHeader = genesisBlock.asHeader(); StoredBlock storedGenesis = new StoredBlock(genesisHeader, genesisHeader.getWork(), 0); put(storedGenesis); - setChainHead(storedGenesis); + chainHead = storedGenesis; } catch (BlockStoreException | VerificationException e) { throw new RuntimeException(e); // Cannot happen. } @@ -55,6 +56,7 @@ public synchronized final void put(StoredBlock block) throws BlockStoreException } @Override + @Nullable public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException { if (blockMap == null) throw new BlockStoreException("MemoryBlockStore is closed"); return blockMap.get(hash); diff --git a/core/src/main/java/org/bitcoinj/store/MemoryFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/MemoryFullPrunedBlockStore.java index b4c2f807b9e..4fdcf268d9f 100644 --- a/core/src/main/java/org/bitcoinj/store/MemoryFullPrunedBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/MemoryFullPrunedBlockStore.java @@ -34,6 +34,7 @@ import org.jspecify.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -194,10 +195,10 @@ protected static class StoredBlockAndWasUndoableFlag { public boolean wasUndoable; public StoredBlockAndWasUndoableFlag(StoredBlock block, boolean wasUndoable) { this.block = block; this.wasUndoable = wasUndoable; } } - private TransactionalHashMap blockMap; - private TransactionalFullBlockMap fullBlockMap; + private @Nullable TransactionalHashMap blockMap; + private @Nullable TransactionalFullBlockMap fullBlockMap; //TODO: Use something more suited to remove-heavy use? - private TransactionalHashMap transactionOutputMap; + private @Nullable TransactionalHashMap transactionOutputMap; private StoredBlock chainHead; private StoredBlock verifiedChainHead; private final int fullStoreDepth; @@ -209,20 +210,19 @@ protected static class StoredBlockAndWasUndoableFlag { * @param fullStoreDepth The depth of blocks to keep FullStoredBlocks instead of StoredBlocks */ public MemoryFullPrunedBlockStore(NetworkParameters params, int fullStoreDepth) { + network = params.network(); blockMap = new TransactionalHashMap<>(); fullBlockMap = new TransactionalFullBlockMap(); transactionOutputMap = new TransactionalHashMap<>(); this.fullStoreDepth = fullStoreDepth > 0 ? fullStoreDepth : 1; // Insert the genesis block. + StoredBlock storedGenesisHeader = new StoredBlock(params.getGenesisBlock().asHeader(), params.getGenesisBlock().getWork(), 0); + // The coinbase in the genesis block is not spendable + StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.getGenesisBlock().getHash(), Collections.emptyList()); try { - StoredBlock storedGenesisHeader = new StoredBlock(params.getGenesisBlock().asHeader(), params.getGenesisBlock().getWork(), 0); - // The coinbase in the genesis block is not spendable - List genesisTransactions = new LinkedList<>(); - StoredUndoableBlock storedGenesis = new StoredUndoableBlock(params.getGenesisBlock().getHash(), genesisTransactions); put(storedGenesisHeader, storedGenesis); - setChainHead(storedGenesisHeader); - setVerifiedChainHead(storedGenesisHeader); - network = params.network(); + chainHead = storedGenesisHeader; + verifiedChainHead = storedGenesisHeader; } catch (BlockStoreException | VerificationException e) { throw new RuntimeException(e); // Cannot happen. } @@ -238,6 +238,8 @@ public synchronized void put(StoredBlock block) throws BlockStoreException { @Override public synchronized final void put(StoredBlock storedBlock, StoredUndoableBlock undoableBlock) throws BlockStoreException { Objects.requireNonNull(blockMap, "MemoryFullPrunedBlockStore is closed"); + Objects.requireNonNull(fullBlockMap); + Objects.requireNonNull(blockMap); Sha256Hash hash = storedBlock.getHeader().getHash(); fullBlockMap.put(hash, storedBlock.getHeight(), undoableBlock); blockMap.put(hash, new StoredBlockAndWasUndoableFlag(storedBlock, true)); @@ -292,6 +294,7 @@ public synchronized final void setVerifiedChainHead(StoredBlock chainHead) throw setChainHead(chainHead); // Potential leak here if not all blocks get setChainHead'd // Though the FullPrunedBlockStore allows for this, the current AbstractBlockChain will not do it. + Objects.requireNonNull(fullBlockMap); fullBlockMap.removeByHeight(chainHead.getHeight() - fullStoreDepth); } @@ -324,6 +327,9 @@ public synchronized void removeUnspentTransactionOutput(UTXO out) throws BlockSt @Override public synchronized void beginDatabaseBatchWrite() throws BlockStoreException { + Objects.requireNonNull(blockMap); + Objects.requireNonNull(fullBlockMap); + Objects.requireNonNull(transactionOutputMap); blockMap.beginDatabaseBatchWrite(); fullBlockMap.BeginTransaction(); transactionOutputMap.beginDatabaseBatchWrite(); @@ -331,6 +337,9 @@ public synchronized void beginDatabaseBatchWrite() throws BlockStoreException { @Override public synchronized void commitDatabaseBatchWrite() throws BlockStoreException { + Objects.requireNonNull(blockMap); + Objects.requireNonNull(fullBlockMap); + Objects.requireNonNull(transactionOutputMap); blockMap.commitDatabaseBatchWrite(); fullBlockMap.CommitTransaction(); transactionOutputMap.commitDatabaseBatchWrite(); @@ -338,6 +347,9 @@ public synchronized void commitDatabaseBatchWrite() throws BlockStoreException { @Override public synchronized void abortDatabaseBatchWrite() throws BlockStoreException { + Objects.requireNonNull(blockMap); + Objects.requireNonNull(fullBlockMap); + Objects.requireNonNull(transactionOutputMap); blockMap.abortDatabaseBatchWrite(); fullBlockMap.AbortTransaction(); transactionOutputMap.abortDatabaseBatchWrite(); @@ -359,7 +371,7 @@ public Network network() { @Override public int getChainHeadHeight() throws UTXOProviderException { try { - return getVerifiedChainHead().getHeight(); + return Objects.requireNonNull(getVerifiedChainHead()).getHeight(); } catch (BlockStoreException e) { throw new UTXOProviderException(e); } @@ -367,6 +379,7 @@ public int getChainHeadHeight() throws UTXOProviderException { @Override public List getOpenTransactionOutputs(List keys) throws UTXOProviderException { + Objects.requireNonNull(transactionOutputMap, "MemoryFullPrunedBlockStore is closed"); // This is *NOT* optimal: We go through all the outputs and select the ones we are looking for. // If someone uses this store for production then they have a lot more to worry about than an inefficient impl :) List foundOutputs = new ArrayList<>(); diff --git a/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java b/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java index e0ac1e52a8f..5359fa897aa 100644 --- a/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java +++ b/core/src/main/java/org/bitcoinj/store/SPVBlockStore.java @@ -28,6 +28,7 @@ import org.jspecify.annotations.Nullable; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.Buffer; @@ -65,7 +66,7 @@ public class SPVBlockStore implements BlockStore { // Magic header for the V2 format. static final byte[] HEADER_MAGIC_V2 = "SPV2".getBytes(StandardCharsets.US_ASCII); - protected volatile MappedByteBuffer buffer; + protected volatile @Nullable MappedByteBuffer buffer; protected final NetworkParameters params; // The entire ring-buffer is mmapped and accessing it should be as fast as accessing regular memory once it's @@ -95,8 +96,8 @@ protected boolean removeEldestEntry(Map.Entry entry) { } }; // Used to stop other applications/processes from opening the store. - protected FileLock fileLock = null; - protected RandomAccessFile randomAccessFile = null; + protected @Nullable FileLock fileLock; + protected final RandomAccessFile randomAccessFile; private final FileChannel channel; private int fileLength; @@ -123,11 +124,16 @@ public SPVBlockStore(NetworkParameters params, File file, int capacity, boolean this.params = Objects.requireNonNull(params); checkArgument(capacity > 0); - try { - boolean exists = file.exists(); + boolean exists = file.exists(); + try { // Set up the backing file, empty if it doesn't exist. randomAccessFile = new RandomAccessFile(file, "rw"); + } catch (FileNotFoundException e) { + throw new BlockStoreException(e); + } + + try { channel = randomAccessFile.getChannel(); // Lock the file. @@ -192,7 +198,7 @@ public SPVBlockStore(NetworkParameters params, File file, int capacity, boolean StandardCharsets.US_ASCII)); } catch (Exception e) { try { - if (randomAccessFile != null) randomAccessFile.close(); + randomAccessFile.close(); } catch (IOException e2) { throw new BlockStoreException(e2); } @@ -201,6 +207,7 @@ public SPVBlockStore(NetworkParameters params, File file, int capacity, boolean } private void initNewStore(Block genesisBlock) throws Exception { + Objects.requireNonNull(buffer); ((Buffer) buffer).rewind(); buffer.put(HEADER_MAGIC_V2); // Insert the genesis block. @@ -225,6 +232,7 @@ private void migrateV1toV2() throws BlockStoreException, IOException { randomAccessFile.setLength(fileLength); // Map it into memory again because of the length change. + Objects.requireNonNull(buffer); buffer.force(); buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, fileLength); @@ -327,7 +335,7 @@ public StoredBlock get(Sha256Hash hash) throws BlockStoreException { } finally { lock.unlock(); } } - protected StoredBlock lastChainHead = null; + protected @Nullable StoredBlock lastChainHead; @Override public StoredBlock getChainHead() throws BlockStoreException { @@ -366,14 +374,19 @@ public void setChainHead(StoredBlock chainHead) throws BlockStoreException { @Override public void close() throws BlockStoreException { - try { + if (buffer != null) { buffer.force(); buffer = null; // Allow it to be GCd and the underlying file mapping to go away. - fileLock.release(); + } + try { + if (fileLock != null) { + fileLock.release(); + } randomAccessFile.close(); - blockCache.clear(); } catch (IOException e) { throw new BlockStoreException(e); + } finally { + blockCache.clear(); } } @@ -395,6 +408,7 @@ public void close() throws BlockStoreException { /** Returns the offset from the file start where the latest block should be written (end of prev block). */ int getRingCursor() { + Objects.requireNonNull(buffer); int c = buffer.getInt(4); checkState(c >= FILE_PROLOGUE_BYTES, () -> "integer overflow"); @@ -403,10 +417,12 @@ int getRingCursor() { private void setRingCursor(int newCursor) { checkArgument(newCursor >= 0); + Objects.requireNonNull(buffer); buffer.putInt(4, newCursor); } public void clear() throws Exception { + Objects.requireNonNull(buffer); lock.lock(); try { // Clear caches diff --git a/core/src/main/java/org/bitcoinj/store/package-info.java b/core/src/main/java/org/bitcoinj/store/package-info.java index 5d4a8c335da..2d5bafd49f1 100644 --- a/core/src/main/java/org/bitcoinj/store/package-info.java +++ b/core/src/main/java/org/bitcoinj/store/package-info.java @@ -19,4 +19,7 @@ * buffer of headers on disk and is suitable for lightweight user wallets, a store that can calculate a full indexed * UTXO set (i.e. it can query address balances), and a memory only store useful for unit tests. */ +@NullMarked package org.bitcoinj.store; + +import org.jspecify.annotations.NullMarked; diff --git a/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java b/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java index 062e719ce59..b0120fed6a7 100644 --- a/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java +++ b/core/src/main/java/org/bitcoinj/utils/BlockFileLoader.java @@ -22,6 +22,7 @@ import org.bitcoinj.core.MessageSerializer; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.ProtocolException; +import org.jspecify.annotations.Nullable; import java.io.BufferedInputStream; import java.io.File; @@ -109,7 +110,7 @@ public BlockFileLoader(Network network, List files) { public class BlockFileIterator implements Iterator { private final File file; private final BufferedInputStream currentFileStream; - private ByteBuffer nextBlock = null; + private @Nullable ByteBuffer nextBlock = null; public BlockFileIterator(File blockFile) throws FileNotFoundException { this.file = blockFile; @@ -124,6 +125,7 @@ public boolean hasNext() { } @Override + @Nullable public ByteBuffer next() throws NoSuchElementException { if (!hasNext()) throw new NoSuchElementException(); diff --git a/core/src/main/java/org/bitcoinj/utils/BtcFormat.java b/core/src/main/java/org/bitcoinj/utils/BtcFormat.java index 2ae6df33977..d473d2bb6b0 100644 --- a/core/src/main/java/org/bitcoinj/utils/BtcFormat.java +++ b/core/src/main/java/org/bitcoinj/utils/BtcFormat.java @@ -18,6 +18,7 @@ import org.bitcoinj.base.Coin; import org.bitcoinj.utils.BtcAutoFormat.Style; +import org.jspecify.annotations.Nullable; import java.math.BigDecimal; import java.math.BigInteger; @@ -1309,6 +1310,7 @@ else if (qty instanceof Coin) * {@link Coin} object that represents the parsed value. * @see NumberFormat */ @Override + @Nullable public final Object parseObject(String source, ParsePosition pos) { return parse(source, pos); } private static class ScaleMatcher { @@ -1320,8 +1322,8 @@ private static class ScaleMatcher { /* Lazy initialization; No reason to create all these objects unless needed for parsing */ // coin indicator regex String; TODO: does this need to be volatile? private volatile String ci = "(" + COIN_SYMBOL + "|" + COIN_SYMBOL_ALT + "|B⃦|" + COIN_CODE + "|XBT)"; - private Pattern coinPattern; - private volatile ScaleMatcher[] denoms; + private @Nullable Pattern coinPattern; + private volatile ScaleMatcher @Nullable [] denoms; ScaleMatcher[] denomMatchers() { ScaleMatcher[] result = denoms; if (result == null) { synchronized(this) { @@ -1412,6 +1414,7 @@ protected static void prefixUnitsIndicator(DecimalFormat numberFormat, int scale * @return a Coin object representing the parsed value * @see java.text.ParsePosition */ + @Nullable public Coin parse(String source, ParsePosition pos) { DecimalFormatSymbols anteSigns = null; int parseScale = COIN_SCALE; // default @@ -1427,6 +1430,7 @@ public Coin parse(String source, ParsePosition pos) { } } if (parseScale == COIN_SCALE) { + Objects.requireNonNull(coinPattern); Matcher matcher = coinPattern.matcher(source); matcher.find(); anteSigns = setSymbolAndCode(numberFormat, matcher.group()); diff --git a/core/src/main/java/org/bitcoinj/utils/VersionTally.java b/core/src/main/java/org/bitcoinj/utils/VersionTally.java index 066b99b6f86..8575a4c9ca0 100644 --- a/core/src/main/java/org/bitcoinj/utils/VersionTally.java +++ b/core/src/main/java/org/bitcoinj/utils/VersionTally.java @@ -20,6 +20,7 @@ import org.bitcoinj.core.StoredBlock; import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; +import org.jspecify.annotations.Nullable; import java.util.Stack; @@ -75,6 +76,7 @@ public void add(final long version) { * @return the count for the block version, or null if the window is not yet * full. */ + @Nullable public Integer getCountAtOrAbove(final long version) { if (versionsStored < versionWindow.length) { return null; diff --git a/core/src/main/java/org/bitcoinj/utils/package-info.java b/core/src/main/java/org/bitcoinj/utils/package-info.java index fe2cc0017d9..7663e129d99 100644 --- a/core/src/main/java/org/bitcoinj/utils/package-info.java +++ b/core/src/main/java/org/bitcoinj/utils/package-info.java @@ -16,4 +16,7 @@ * Formatting monetary amounts, representing exchange rates, a program for loading Bitcoin Core saved block files, * a class to control how bitcoinj uses threads and misc other utility classes that don't fit anywhere else. */ -package org.bitcoinj.utils; \ No newline at end of file +@NullMarked +package org.bitcoinj.utils; + +import org.jspecify.annotations.NullMarked; diff --git a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java index e542f83e600..cc08ab38ca5 100644 --- a/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java +++ b/core/src/test/java/org/bitcoinj/store/WalletProtobufSerializerTest.java @@ -192,7 +192,9 @@ public void doubleSpend() throws Exception { myWallet.receiveFromBlock(doubleSpends.t2, null, BlockChain.NewBlockType.BEST_CHAIN, 0); Wallet wallet1 = roundTrip(myWallet); assertEquals(1, wallet1.getTransactions(true).size()); + assertNotNull(doubleSpends.t1); Transaction t1 = wallet1.getTransaction(doubleSpends.t1.getTxId()); + assertNotNull(t1); assertEquals(ConfidenceType.DEAD, t1.getConfidence().getConfidenceType()); assertEquals(Coin.ZERO, wallet1.getBalance()); @@ -408,7 +410,7 @@ public void extensions() throws Exception { new WalletProtobufSerializer().readWallet(BitcoinNetwork.TESTNET, null, proto); fail(); } catch (UnreadableWalletException e) { - assertTrue(e.getMessage().contains("mandatory")); + assertTrue(Objects.requireNonNull(e.getMessage()).contains("mandatory")); } Wallet wallet = new WalletProtobufSerializer().readWallet(BitcoinNetwork.TESTNET, new WalletExtension[]{ new FooWalletExtension("com.whatever.required", true) }, diff --git a/core/src/test/java/org/bitcoinj/utils/ExchangeRateTest.java b/core/src/test/java/org/bitcoinj/utils/ExchangeRateTest.java index 5740c20cae2..e4303daccab 100644 --- a/core/src/test/java/org/bitcoinj/utils/ExchangeRateTest.java +++ b/core/src/test/java/org/bitcoinj/utils/ExchangeRateTest.java @@ -51,11 +51,6 @@ public void currencyCodeMismatch() { rate.fiatToCoin(Fiat.parseFiat("USD", "1")); } - @Test(expected = NullPointerException.class) - public void constructMissingCurrencyCode() { - new ExchangeRate(Fiat.valueOf(null, 1)); - } - @Test(expected = IllegalArgumentException.class) public void constructNegativeCoin() { new ExchangeRate(Coin.valueOf(-1), Fiat.valueOf("EUR", 1)); diff --git a/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java b/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java index d3f4c68fba8..8c71283ccc5 100644 --- a/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java +++ b/core/src/test/java/org/bitcoinj/utils/VersionTallyTest.java @@ -34,8 +34,10 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Objects; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; public class VersionTallyTest { @@ -64,7 +66,7 @@ public void testNullWhileEmpty() { assertNull(instance.getCountAtOrAbove(1)); instance.add(1); } - assertEquals(TESTNET.getMajorityWindow(), instance.getCountAtOrAbove(1).intValue()); + assertEquals(TESTNET.getMajorityWindow(), Objects.requireNonNull(instance.getCountAtOrAbove(1)).intValue()); } /** @@ -88,21 +90,21 @@ public void testVersionCounts() { assertNull(instance.getCountAtOrAbove(1)); instance.add(1); } - assertEquals(TESTNET.getMajorityWindow(), instance.getCountAtOrAbove(1).intValue()); + assertEquals(TESTNET.getMajorityWindow(), Objects.requireNonNull(instance.getCountAtOrAbove(1)).intValue()); // Check the count updates as we replace with 2s for (int i = 0; i < TESTNET.getMajorityWindow(); i++) { - assertEquals(i, instance.getCountAtOrAbove(2).intValue()); + assertEquals(i, Objects.requireNonNull(instance.getCountAtOrAbove(2)).intValue()); instance.add(2); } // Inject a rogue 1 instance.add(1); - assertEquals(TESTNET.getMajorityWindow() - 1, instance.getCountAtOrAbove(2).intValue()); + assertEquals(TESTNET.getMajorityWindow() - 1, Objects.requireNonNull(instance.getCountAtOrAbove(2)).intValue()); // Check we accept high values as well instance.add(10); - assertEquals(TESTNET.getMajorityWindow() - 1, instance.getCountAtOrAbove(2).intValue()); + assertEquals(TESTNET.getMajorityWindow() - 1, Objects.requireNonNull(instance.getCountAtOrAbove(2)).intValue()); } @Test @@ -119,9 +121,10 @@ public void testInitialize() throws BlockStoreException { assertEquals(2, chainHead.getHeader().version()); time = time.plus(1, ChronoUnit.MINUTES); } + assertNotNull(chainHead); VersionTally instance = new VersionTally(TESTNET); instance.initialize(blockStore, chainHead); - assertEquals(TESTNET.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue()); + assertEquals(TESTNET.getMajorityWindow(), Objects.requireNonNull(instance.getCountAtOrAbove(2)).intValue()); } }