Add BIP 34 enforcement

This patch primarily enforces block height being present in the coinbase
transaction input, altough it introduces a number of other fixes and
changes to support this.

* VersionTally now returns the number of blocks at or above a version, rather than just at, to enable forward-compatible support (i.e. v3 blocks include all v2 block tests)
* Block version is now explicitely provided in most tests which generate blocks, in order to ensure correct tests are applied
* Block height is now used when generating coinbase transactions
* Added support for the chain parameters to determine which tests apply to a block, so altcoins can override the defaults if needed.
* Added initial checks ahead of full BIP 66 validation checks
This commit is contained in:
Ross Nicoll
2015-08-22 21:00:26 +01:00
committed by Mike Hearn
parent c5727d12fa
commit d3d11df6d7
27 changed files with 387 additions and 111 deletions

View File

@@ -436,14 +436,25 @@ public abstract class AbstractBlockChain {
return true;
}
final StoredBlock storedPrev;
final int height;
final EnumSet<VerificationFlags> flags;
// Prove the block is internally valid: hash is lower than target, etc. This only checks the block contents
// if there is a tx sending or receiving coins using an address in one of our wallets. And those transactions
// are only lightly verified: presence in a valid connecting block is taken as proof of validity. See the
// article here for more details: http://code.google.com/p/bitcoinj/wiki/SecurityModel
try {
block.verifyHeader();
storedPrev = getStoredBlockInCurrentScope(block.getPrevBlockHash());
if (storedPrev != null) {
height = storedPrev.getHeight() + 1;
} else {
height = Block.BLOCK_HEIGHT_UNKNOWN;
}
flags = params.getValidationFlags(block, versionTally, height);
if (shouldVerifyTransactions())
block.verifyTransactions();
block.verifyTransactions(height, flags);
} catch (VerificationException e) {
log.error("Failed to verify block: ", e);
log.error(block.getHashAsString());
@@ -451,7 +462,6 @@ public abstract class AbstractBlockChain {
}
// Try linking it to a place in the currently known blocks.
StoredBlock storedPrev = getStoredBlockInCurrentScope(block.getPrevBlockHash());
if (storedPrev == null) {
// We can't find the previous block. Probably we are still in the process of downloading the chain and a
@@ -526,7 +536,7 @@ public abstract class AbstractBlockChain {
// net, less on test) in order to be applied. It is also limited to
// stopping addition of new v2 blocks to the tip of the chain.
if (block.getVersion() == Block.BLOCK_VERSION_BIP34) {
final Integer count = versionTally.getCount(Block.BLOCK_VERSION_BIP66);
final Integer count = versionTally.getCountAtOrAbove(Block.BLOCK_VERSION_BIP66);
if (count != null
&& count >= params.getMajorityRejectBlockOutdated()) {
throw new VerificationException.BlockVersionOutOfDate(block.getVersion());

View File

@@ -30,13 +30,18 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import static org.bitcoinj.core.Coin.FIFTY_COINS;
import static org.bitcoinj.core.Sha256Hash.hashTwice;
import org.bitcoinj.script.ScriptChunk;
/**
* <p>A block is a group of transactions, and is one of the fundamental data structures of the Bitcoin system.
@@ -72,6 +77,11 @@ public class Block extends Message {
/** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */
public static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
/** Value to use if the block height is unknown */
public static final int BLOCK_HEIGHT_UNKNOWN = -1;
/** Height of the first block */
public static final int BLOCK_HEIGHT_GENESIS = 0;
public static final long BLOCK_VERSION_GENESIS = 1;
/** Block version introduced in BIP 34: Height in coinbase */
public static final long BLOCK_VERSION_BIP34 = 2;
@@ -610,10 +620,21 @@ public class Block extends Message {
return tree;
}
private void checkTransactions() throws VerificationException {
/**
* Verify the transactions on a block.
*
* @param height block height, if known, or -1 otherwise. If provided, used
* to validate the coinbase input script of v2 and above blocks.
* @throws VerificationException if there was an error verifying the block.
*/
private void checkTransactions(final int height, final EnumSet<VerificationFlags> flags)
throws VerificationException {
// The first transaction in a block must always be a coinbase transaction.
if (!transactions.get(0).isCoinBase())
throw new VerificationException("First tx is not coinbase");
if (flags.contains(VerificationFlags.HEIGHT_IN_COINBASE) && height >= BLOCK_HEIGHT_GENESIS) {
transactions.get(0).checkCoinBaseHeight(height);
}
// The rest must not be.
for (int i = 1; i < transactions.size(); i++) {
if (transactions.get(i).isCoinBase())
@@ -642,9 +663,13 @@ public class Block extends Message {
/**
* Checks the block contents
*
* @throws VerificationException
* @param height block height, if known, or -1 otherwise. If valid, used
* to validate the coinbase input script of v2 and above blocks.
* @param flags flags to indicate which tests should be applied (i.e.
* whether to test for height in the coinbase transaction).
* @throws VerificationException if there was an error verifying the block.
*/
public void verifyTransactions() throws VerificationException {
public void verifyTransactions(final int height, final EnumSet<VerificationFlags> flags) throws VerificationException {
// Now we need to check that the body of the block actually matches the headers. The network won't generate
// an invalid block, but if we didn't validate this then an untrusted man-in-the-middle could obtain the next
// valid block from the network and simply replace the transactions in it with their own fictional
@@ -653,7 +678,7 @@ public class Block extends Message {
throw new VerificationException("Block had no transactions");
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
checkTransactions();
checkTransactions(height, flags);
checkMerkleRoot();
checkSigOps();
for (Transaction transaction : transactions)
@@ -662,10 +687,15 @@ public class Block extends Message {
/**
* Verifies both the header and that the transactions hash to the merkle root.
*
* @param height block height, if known, or -1 otherwise.
* @param flags flags to indicate which tests should be applied (i.e.
* whether to test for height in the coinbase transaction).
* @throws VerificationException if there was an error verifying the block.
*/
public void verify() throws VerificationException {
public void verify(final int height, final EnumSet<VerificationFlags> flags) throws VerificationException {
verifyHeader();
verifyTransactions();
verifyTransactions(height, flags);
}
@Override
@@ -808,19 +838,30 @@ public class Block extends Message {
// Used to make transactions unique.
private static int txCounter;
/** Adds a coinbase transaction to the block. This exists for unit tests. */
/** Adds a coinbase transaction to the block. This exists for unit tests.
*
* @param height block height, if known, or -1 otherwise.
*/
@VisibleForTesting
void addCoinbaseTransaction(byte[] pubKeyTo, Coin value) {
void addCoinbaseTransaction(byte[] pubKeyTo, Coin value, final int height) {
unCacheTransactions();
transactions = new ArrayList<Transaction>();
Transaction coinbase = new Transaction(params);
final ScriptBuilder inputBuilder = new ScriptBuilder();
if (height >= Block.BLOCK_HEIGHT_GENESIS) {
final byte[] blockHeightBytes = ScriptBuilder.createHeightScriptData(height);
inputBuilder.data(blockHeightBytes);
}
inputBuilder.data(new byte[]{(byte) txCounter, (byte) (txCounter++ >> 8)});
// A real coinbase transaction has some stuff in the scriptSig like the extraNonce and difficulty. The
// transactions are distinguished by every TX output going to a different key.
//
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
// counter in the scriptSig so every transaction has a different hash.
coinbase.addInput(new TransactionInput(params, coinbase,
new ScriptBuilder().data(new byte[]{(byte) txCounter, (byte) (txCounter++ >> 8)}).build().getProgram()));
inputBuilder.build().getProgram()));
coinbase.addOutput(new TransactionOutput(params, coinbase, value,
ScriptBuilder.createOutputScript(ECKey.fromPublicOnly(pubKeyTo)).getProgram()));
transactions.add(coinbase);
@@ -838,19 +879,24 @@ public class Block extends Message {
* Returns a solved block that builds on top of this one. This exists for unit tests.
*/
@VisibleForTesting
public Block createNextBlock(Address to, long version, long time) {
return createNextBlock(to, version, null, time, pubkeyForTesting, FIFTY_COINS);
public Block createNextBlock(Address to, long version, long time, Integer blockHeight) {
return createNextBlock(to, version, (TransactionOutPoint) null, time,
pubkeyForTesting, FIFTY_COINS, blockHeight);
}
/**
* Returns a solved block that builds on top of this one. This exists for unit tests.
* In this variant you can specify a public key (pubkey) for use in generating coinbase blocks.
*
* @param height block height, if known, or -1 otherwise.
*/
Block createNextBlock(@Nullable Address to, long version, @Nullable TransactionOutPoint prevOut,
long time, byte[] pubKey, Coin coinbaseValue) {
Block createNextBlock(@Nullable final Address to, final long version,
@Nullable TransactionOutPoint prevOut, final long time,
final byte[] pubKey, final Coin coinbaseValue,
final int height) {
Block b = new Block(params, version);
b.setDifficultyTarget(difficultyTarget);
b.addCoinbaseTransaction(pubKey, coinbaseValue);
b.addCoinbaseTransaction(pubKey, coinbaseValue, height);
if (to != null) {
// Add a transaction paying 50 coins to the "to" address.
@@ -885,17 +931,20 @@ public class Block extends Message {
} catch (VerificationException e) {
throw new RuntimeException(e); // Cannot happen.
}
if (b.getVersion() != version) {
throw new RuntimeException();
}
return b;
}
@VisibleForTesting
public Block createNextBlock(@Nullable Address to, TransactionOutPoint prevOut) {
return createNextBlock(to, 1, prevOut, getTimeSeconds() + 5, pubkeyForTesting, FIFTY_COINS);
return createNextBlock(to, BLOCK_VERSION_GENESIS, prevOut, getTimeSeconds() + 5, pubkeyForTesting, FIFTY_COINS, BLOCK_HEIGHT_UNKNOWN);
}
@VisibleForTesting
public Block createNextBlock(@Nullable Address to, Coin value) {
return createNextBlock(to, 1, null, getTimeSeconds() + 5, pubkeyForTesting, value);
return createNextBlock(to, BLOCK_VERSION_GENESIS, null, getTimeSeconds() + 5, pubkeyForTesting, value, BLOCK_HEIGHT_UNKNOWN);
}
@VisibleForTesting
@@ -904,8 +953,9 @@ public class Block extends Message {
}
@VisibleForTesting
public Block createNextBlockWithCoinbase(byte[] pubKey, Coin coinbaseValue) {
return createNextBlock(null, 1, null, Utils.currentTimeSeconds(), pubKey, coinbaseValue);
public Block createNextBlockWithCoinbase(long version, byte[] pubKey, Coin coinbaseValue, final int height) {
return createNextBlock(null, version, (TransactionOutPoint) null,
Utils.currentTimeSeconds(), pubKey, coinbaseValue, height);
}
/**
@@ -913,8 +963,9 @@ public class Block extends Message {
* This method is intended for test use only.
*/
@VisibleForTesting
Block createNextBlockWithCoinbase(byte[] pubKey) {
return createNextBlock(null, 1, null, Utils.currentTimeSeconds(), pubKey, FIFTY_COINS);
Block createNextBlockWithCoinbase(long version, byte[] pubKey, final int height) {
return createNextBlock(null, version, (TransactionOutPoint) null,
Utils.currentTimeSeconds(), pubKey, FIFTY_COINS, height);
}
@VisibleForTesting

View File

@@ -35,6 +35,7 @@ import java.math.*;
import java.util.*;
import static org.bitcoinj.core.Coin.*;
import org.bitcoinj.utils.VersionTally;
/**
* <p>NetworkParameters contains the data needed for working with an instantiation of a Bitcoin chain.</p>
@@ -476,4 +477,32 @@ public abstract class NetworkParameters {
public int getMajorityWindow() {
return majorityWindow;
}
/**
* The flags indicating which block validation tests should be applied to
* the given block. Enables support for alternative blockchains which enable
* tests based on different criteria.
*
* @param block block to determine flags for.
* @param height height of the block, if known, null otherwise. Returned
* tests should be a safe subset if block height is unknown.
*/
public EnumSet<VerificationFlags> getValidationFlags(final Block block,
final VersionTally tally, final Integer height) {
final EnumSet<VerificationFlags> flags = EnumSet.noneOf(VerificationFlags.class);
if (block.getVersion() >= Block.BLOCK_VERSION_BIP34) {
final Integer count = tally.getCountAtOrAbove(Block.BLOCK_VERSION_BIP34);
if (null != count && count >= getMajorityEnforceBlockUpgrade()) {
flags.add(VerificationFlags.HEIGHT_IN_COINBASE);
}
}
if (block.getVersion() >= Block.BLOCK_VERSION_BIP66) {
final Integer count = tally.getCountAtOrAbove(Block.BLOCK_VERSION_BIP66);
if (null != count && count >= getMajorityEnforceBlockUpgrade()) {
flags.add(VerificationFlags.DER_SIGNATURE_FORMAT);
}
}
return flags;
}
}

View File

@@ -36,6 +36,9 @@ import java.util.*;
import static org.bitcoinj.core.Utils.*;
import static com.google.common.base.Preconditions.checkState;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.bitcoinj.script.ScriptChunk;
/**
* <p>A transaction represents the movement of coins from some addresses to some other addresses. It can also represent
@@ -1163,6 +1166,39 @@ public class Transaction extends ChildMessage {
return sigOps;
}
/**
* Check block height is in coinbase input script, for use after BIP 34
* enforcement is enabled.
*/
public void checkCoinBaseHeight(final int height)
throws VerificationException {
assert height >= Block.BLOCK_HEIGHT_GENESIS;
assert isCoinBase();
// Check block height is in coinbase input script
final TransactionInput in = this.getInputs().get(0);
final List<ScriptChunk> chunks;
try {
final Script scriptSig = in.getScriptSig();
chunks = scriptSig.getChunks();
} catch(ScriptException e) {
throw new VerificationException("Coinbase input script signature is invalid.", e);
}
if (chunks.isEmpty()) {
throw new VerificationException("Coinbase input script signature is empty.");
}
final ScriptChunk chunk = chunks.get(0);
if (!chunk.isPushData()) {
throw new VerificationException("First element of coinbase input script signature is not pushdata.");
}
final byte[] data = chunk.data;
final byte[] expected = ScriptBuilder.createHeightScriptData(height);
if (!Arrays.equals(data, expected)) {
throw new VerificationException.CoinbaseHeightMismatch("Coinbase height mismatch.");
}
}
/**
* <p>Checks the transaction contents for sanity, in ways that can be done in a standalone manner.
* Does <b>not</b> perform all checks on a transaction such as whether the inputs are already spent.

View File

@@ -80,4 +80,10 @@ public class VerificationException extends RuntimeException {
super("Coinbase input as input in non-coinbase transaction");
}
}
public static class CoinbaseHeightMismatch extends VerificationException {
public CoinbaseHeightMismatch(final String message) {
super(message);
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2015 Ross Nicoll
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
/**
* Flags used to control which elements of block validation are performed on
* received blocks.
*/
public enum VerificationFlags {
/** Check that block height is in coinbase transaction (BIP 34) */
HEIGHT_IN_COINBASE,
/** Check DER signature format is exact (BIP 66) */
DER_SIGNATURE_FORMAT
}

View File

@@ -18,6 +18,8 @@
package org.bitcoinj.params;
import java.math.BigInteger;
import java.util.EnumSet;
import java.util.Set;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.Coin;
@@ -33,6 +35,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.bitcoinj.core.BitcoinSerializer;
import org.bitcoinj.core.VerificationFlags;
import org.bitcoinj.utils.VersionTally;
/**
* Parameters for Bitcoin-like networks.

View File

@@ -30,6 +30,8 @@ import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static org.bitcoinj.script.ScriptOpCodes.*;
/**
@@ -344,4 +346,17 @@ public class ScriptBuilder {
checkArgument(data.length <= 40);
return new ScriptBuilder().op(OP_RETURN).data(data).build();
}
/**
* Create script data bytes to represent the given block height.
*/
public static byte[] createHeightScriptData(final int height) {
// TODO: Replace with something generic to any integer value
final byte[] int32Buffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(height).array();
if (int32Buffer[3] == 0) {
return Arrays.copyOf(int32Buffer, 3);
} else {
return int32Buffer;
}
}
}

View File

@@ -167,12 +167,13 @@ public class FakeTxBuilder {
}
/** Emulates receiving a valid block that builds on top of the chain. */
public static BlockPair createFakeBlock(BlockStore blockStore, long version, long timeSeconds,
public static BlockPair createFakeBlock(BlockStore blockStore, long version,
long timeSeconds, int height,
Transaction... transactions) {
try {
Block chainHead = blockStore.getChainHead().getHeader();
Address to = new ECKey().toAddress(chainHead.getParams());
Block b = chainHead.createNextBlock(to, version, timeSeconds);
Block b = chainHead.createNextBlock(to, version, timeSeconds, height);
// Coinbase tx was already added.
for (Transaction tx : transactions) {
tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
@@ -193,8 +194,9 @@ public class FakeTxBuilder {
}
/** Emulates receiving a valid block that builds on top of the chain. */
public static BlockPair createFakeBlock(BlockStore blockStore, Transaction... transactions) {
return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), transactions);
public static BlockPair createFakeBlock(BlockStore blockStore, Integer height,
Transaction... transactions) {
return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), height, transactions);
}
public static Block makeSolvedTestBlock(BlockStore blockStore, Address coinsTo) throws BlockStoreException {

View File

@@ -68,7 +68,7 @@ public class TestWithWallet {
if (wallet.isPendingTransactionRelevant(tx))
wallet.receivePending(tx, null);
} else {
FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, tx);
FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, tx);
wallet.receiveFromBlock(tx, bp.storedBlock, type, 0);
if (type == AbstractBlockChain.NewBlockType.BEST_CHAIN)
wallet.notifyNewBestBlock(bp.storedBlock);

View File

@@ -68,19 +68,19 @@ public class VersionTally {
}
/**
* Get the count for a block version within the window.
* Get the count of blocks at or above the given version, within the window.
*
* @param version the block version to query.
* @return the count for the block version, or null if the window is not yet
* full.
*/
public Integer getCount(final long version) {
public Integer getCountAtOrAbove(final long version) {
if (versionsStored < versionWindow.length) {
return null;
}
int count = 0;
for (int versionIdx = 0; versionIdx < versionWindow.length; versionIdx++) {
if (versionWindow[versionIdx] == version) {
if (versionWindow[versionIdx] >= version) {
count++;
}
}

View File

@@ -37,13 +37,20 @@ import java.util.Arrays;
import java.util.List;
import static org.bitcoinj.core.Coin.FIFTY_COINS;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.MemoryBlockStore;
import org.bitcoinj.testing.FakeTxBuilder;
import static org.junit.Assert.*;
import org.junit.rules.ExpectedException;
/**
* We don't do any wallet tests here, we leave that to {@link ChainSplitTest}
*/
public abstract class AbstractFullPrunedBlockChainTest {
@org.junit.Rule
public ExpectedException thrown = ExpectedException.none();
private static final Logger log = LoggerFactory.getLogger(AbstractFullPrunedBlockChainTest.class);
protected NetworkParameters params;
@@ -125,13 +132,14 @@ public abstract class AbstractFullPrunedBlockChainTest {
// to the full StoredUndoableBlock's lying around (ie memory leaks)
ECKey outKey = new ECKey();
int height = 1;
// Build some blocks on genesis block to create a spendable output
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey());
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0);
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey());
rollingBlock = rollingBlock.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
}
@@ -164,14 +172,15 @@ public abstract class AbstractFullPrunedBlockChainTest {
// to the full StoredUndoableBlock's lying around (ie memory leaks)
ECKey outKey = new ECKey();
int height = 1;
// Build some blocks on genesis block to create a spendable output
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey());
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, rollingBlock.getTransactions().get(0).getHash());
byte[] spendableOutputScriptPubKey = rollingBlock.getTransactions().get(0).getOutputs().get(0).getScriptBytes();
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey());
rollingBlock = rollingBlock.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
}
@@ -237,15 +246,16 @@ public abstract class AbstractFullPrunedBlockChainTest {
// Check that we aren't accidentally leaving any references
// to the full StoredUndoableBlock's lying around (ie memory leaks)
ECKey outKey = new ECKey();
int height = 1;
// Build some blocks on genesis block to create a spendable output
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey());
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
Transaction transaction = rollingBlock.getTransactions().get(0);
TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey());
rollingBlock = rollingBlock.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
}
rollingBlock = rollingBlock.createNextBlock(null);
@@ -287,15 +297,16 @@ public abstract class AbstractFullPrunedBlockChainTest {
// Check that we aren't accidentally leaving any references
// to the full StoredUndoableBlock's lying around (ie memory leaks)
ECKey outKey = new ECKey();
int height = 1;
// Build some blocks on genesis block to create a spendable output.
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(outKey.getPubKey());
Block rollingBlock = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
Transaction transaction = rollingBlock.getTransactions().get(0);
TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
rollingBlock = rollingBlock.createNextBlockWithCoinbase(outKey.getPubKey());
rollingBlock = rollingBlock.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++);
chain.add(rollingBlock);
}
rollingBlock = rollingBlock.createNextBlock(null);
@@ -340,4 +351,58 @@ public abstract class AbstractFullPrunedBlockChainTest {
store.close();
} catch (Exception e) {}
}
/**
* Test that if the block height is missing from coinbase of a version 2
* block, it's rejected.
*/
@Test
public void missingHeightFromCoinbase() throws Exception {
final int UNDOABLE_BLOCKS_STORED = params.getMajorityEnforceBlockUpgrade() + 1;
store = createStore(params, UNDOABLE_BLOCKS_STORED);
try {
chain = new FullPrunedBlockChain(params, store);
ECKey outKey = new ECKey();
int height = 1;
Block chainHead = params.getGenesisBlock();
// Build some blocks on genesis block to create a spendable output.
// Put in just enough v1 blocks to stop the v2 blocks from forming a majority
for (height = 1; height <= (params.getMajorityWindow() - params.getMajorityEnforceBlockUpgrade()); height++) {
chainHead = chainHead.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS,
outKey.getPubKey(), height);
chain.add(chainHead);
}
// Fill the rest of the window in with v2 blocks
for (; height < params.getMajorityWindow(); height++) {
chainHead = chainHead.createNextBlockWithCoinbase(Block.BLOCK_VERSION_BIP34,
outKey.getPubKey(), height);
chain.add(chainHead);
}
// Throw a broken v2 block in before we have a supermajority to enable
// enforcement, which should validate as-is
chainHead = chainHead.createNextBlockWithCoinbase(Block.BLOCK_VERSION_BIP34,
outKey.getPubKey(), height * 2);
chain.add(chainHead);
height++;
// Trying to add a broken v2 block should now result in rejection as
// we have a v2 supermajority
thrown.expect(VerificationException.CoinbaseHeightMismatch.class);
chainHead = chainHead.createNextBlockWithCoinbase(Block.BLOCK_VERSION_BIP34,
outKey.getPubKey(), height * 2);
chain.add(chainHead);
} catch(final VerificationException ex) {
throw (Exception) ex.getCause();
} finally {
try {
store.close();
} catch(Exception e) {
// Catch and drop any exception so a break mid-test doesn't result
// in a new exception being thrown and the original lost
}
}
}
}

View File

@@ -131,11 +131,12 @@ public class BlockChainTest {
@Test
public void receiveCoins() throws Exception {
int height = 1;
// Quick check that we can actually receive coins.
Transaction tx1 = createFakeTx(unitTestParams,
COIN,
wallet.currentReceiveKey().toAddress(unitTestParams));
Block b1 = createFakeBlock(blockStore, tx1).block;
Block b1 = createFakeBlock(blockStore, height, tx1).block;
chain.add(b1);
assertTrue(wallet.getBalance().signum() > 0);
}
@@ -161,8 +162,8 @@ public class BlockChainTest {
// artificially shortened period.
Block prev = unitTestParams.getGenesisBlock();
Utils.setMockClock(System.currentTimeMillis()/1000);
for (int i = 0; i < unitTestParams.getInterval() - 1; i++) {
Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds());
for (int height = 0; height < unitTestParams.getInterval() - 1; height++) {
Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
assertTrue(chain.add(newBlock));
prev = newBlock;
// The fake chain should seem to be "fast" for the purposes of difficulty calculations.
@@ -170,13 +171,13 @@ public class BlockChainTest {
}
// Now add another block that has no difficulty adjustment, it should be rejected.
try {
chain.add(prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds()));
chain.add(prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), unitTestParams.getInterval()));
fail();
} catch (VerificationException e) {
}
// Create a new block with the right difficulty target given our blistering speed relative to the huge amount
// of time it's supposed to take (set in the unit test network parameters).
Block b = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds());
Block b = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), unitTestParams.getInterval() + 1);
b.setDifficultyTarget(0x201fFFFFL);
b.solve();
assertTrue(chain.add(b));
@@ -234,23 +235,23 @@ public class BlockChainTest {
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int blockCount = 0;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (blockCount = 0; blockCount < (unitTestParams.getMajorityWindow() - unitTestParams.getMajorityRejectBlockOutdated()); blockCount++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP34, timeSeconds);
for (height = 0; height < (unitTestParams.getMajorityWindow() - unitTestParams.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP34, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; blockCount < unitTestParams.getMajorityWindow(); blockCount++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP66, timeSeconds);
for (; height < unitTestParams.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP66, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP34, timeSeconds);
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, Block.BLOCK_VERSION_BIP34, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.class);
try {
@@ -306,12 +307,13 @@ public class BlockChainTest {
// Create a second wallet to receive the coinbase spend.
Wallet wallet2 = new Wallet(unitTestParams);
ECKey receiveKey = wallet2.freshReceiveKey();
int height = 1;
chain.addWallet(wallet2);
Address addressToSendTo = receiveKey.toAddress(unitTestParams);
// Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
Block b1 = unitTestParams.getGenesisBlock().createNextBlockWithCoinbase(wallet.currentReceiveKey().getPubKey());
Block b1 = unitTestParams.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
chain.add(b1);
// Check a transaction has been received.
@@ -335,7 +337,7 @@ public class BlockChainTest {
Transaction tx2 = createFakeTx(unitTestParams, COIN,
new ECKey().toAddress(unitTestParams));
Block b2 = createFakeBlock(blockStore, tx2).block;
Block b2 = createFakeBlock(blockStore, height++, tx2).block;
chain.add(b2);
// Wallet still does not have the coinbase transaction available for spend.
@@ -355,7 +357,7 @@ public class BlockChainTest {
// Give it one more block - should now be able to spend coinbase transaction. Non relevant tx.
Transaction tx3 = createFakeTx(unitTestParams, COIN, new ECKey().toAddress(unitTestParams));
Block b3 = createFakeBlock(blockStore, tx3).block;
Block b3 = createFakeBlock(blockStore, height++, tx3).block;
chain.add(b3);
// Wallet now has the coinbase transaction available for spend.
@@ -374,7 +376,7 @@ public class BlockChainTest {
assertEquals(wallet.getBalance(BalanceType.AVAILABLE), ZERO);
// Give it one more block - change from coinbaseSpend should now be available in the first wallet.
Block b4 = createFakeBlock(blockStore, coinbaseSend2).block;
Block b4 = createFakeBlock(blockStore, height++, coinbaseSend2).block;
chain.add(b4);
assertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);

View File

@@ -25,6 +25,7 @@ import org.junit.Test;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.EnumSet;
import static org.bitcoinj.core.Utils.HEX;
import static org.junit.Assert.*;
@@ -57,7 +58,7 @@ public class BlockTest {
@Test
public void testBlockVerification() throws Exception {
Block block = params.getDefaultSerializer().makeBlock(blockBytes);
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
assertEquals("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString());
}
@@ -75,7 +76,7 @@ public class BlockTest {
Block block = params.getDefaultSerializer().makeBlock(blockBytes);
block.setNonce(12346);
try {
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
fail();
} catch (VerificationException e) {
// Expected.
@@ -84,18 +85,18 @@ public class BlockTest {
// from containing artificially weak difficulties.
block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
// Now it should pass.
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
// Break the nonce again at the lower difficulty level so we can try solving for it.
block.setNonce(1);
try {
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
fail();
} catch (VerificationException e) {
// Expected to fail as the nonce is no longer correct.
}
// Should find an acceptable nonce.
block.solve();
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
assertEquals(block.getNonce(), 2);
}
@@ -108,7 +109,7 @@ public class BlockTest {
block.transactions.set(0, tx2);
block.transactions.set(1, tx1);
try {
block.verify();
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(VerificationFlags.class));
fail();
} catch (VerificationException e) {
// We should get here.
@@ -136,7 +137,7 @@ public class BlockTest {
@Test
public void testUpdateLength() {
NetworkParameters params = UnitTestParams.get();
Block block = params.getGenesisBlock().createNextBlockWithCoinbase(new ECKey().getPubKey());
Block block = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, new ECKey().getPubKey(), Block.BLOCK_HEIGHT_GENESIS);
assertEquals(block.bitcoinSerialize().length, block.length);
final int origBlockLen = block.length;
Transaction tx = new Transaction(params);

View File

@@ -566,7 +566,7 @@ public class ChainSplitTest {
Block b1 = unitTestParams.getGenesisBlock().createNextBlock(someOtherGuy);
final ECKey coinsTo2 = wallet.freshReceiveKey();
Block b2 = b1.createNextBlockWithCoinbase(coinsTo2.getPubKey());
Block b2 = b1.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, coinsTo2.getPubKey(), 2);
Block b3 = b2.createNextBlock(someOtherGuy);
log.debug("Adding block b1");

View File

@@ -26,6 +26,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.EnumSet;
import java.util.List;
import static org.junit.Assert.assertEquals;
@@ -63,7 +64,7 @@ public class CoinbaseBlockTest {
// Check block.
assertNotNull(block);
block.verify();
block.verify(169482, EnumSet.noneOf(VerificationFlags.class));
assertEquals(BLOCK_NONCE, block.getNonce());
StoredBlock storedBlock = new StoredBlock(block, BigInteger.ONE, BLOCK_OF_INTEREST); // Nonsense work - not used in test.

View File

@@ -177,13 +177,13 @@ public class FullBlockTestGenerator {
Queue<TransactionOutPointWithValue> spendableOutputs = new LinkedList<TransactionOutPointWithValue>();
int chainHeadHeight = 1;
Block chainHead = params.getGenesisBlock().createNextBlockWithCoinbase(coinbaseOutKeyPubKey);
Block chainHead = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, coinbaseOutKeyPubKey, chainHeadHeight);
blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), 1, "Initial Block"));
spendableOutputs.offer(new TransactionOutPointWithValue(
new TransactionOutPoint(params, 0, chainHead.getTransactions().get(0).getHash()),
FIFTY_COINS, chainHead.getTransactions().get(0).getOutputs().get(0).getScriptPubKey()));
for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) {
chainHead = chainHead.createNextBlockWithCoinbase(coinbaseOutKeyPubKey);
chainHead = chainHead.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, coinbaseOutKeyPubKey, chainHeadHeight);
chainHeadHeight++;
blocks.add(new BlockAndValidity(chainHead, true, false, chainHead.getHash(), i+1, "Initial Block chain output generation"));
spendableOutputs.offer(new TransactionOutPointWithValue(
@@ -894,7 +894,7 @@ public class FullBlockTestGenerator {
byte[] outScriptBytes = ScriptBuilder.createOutputScript(ECKey.fromPublicOnly(coinbaseOutKeyPubKey)).getProgram();
{
b44.setDifficultyTarget(b43.block.getDifficultyTarget());
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, ZERO);
b44.addCoinbaseTransaction(coinbaseOutKeyPubKey, ZERO, chainHeadHeight + 15);
Transaction t = new Transaction(params);
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much
@@ -1768,7 +1768,7 @@ public class FullBlockTestGenerator {
Coin coinbaseValue = FIFTY_COINS.shiftRight(nextBlockHeight / params.getSubsidyDecreaseBlockCount())
.add((prevOut != null ? prevOut.value.subtract(SATOSHI) : ZERO))
.add(additionalCoinbaseValue == null ? ZERO : additionalCoinbaseValue);
Block block = baseBlock.createNextBlockWithCoinbase(coinbaseOutKeyPubKey, coinbaseValue);
Block block = baseBlock.createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, coinbaseOutKeyPubKey, coinbaseValue, nextBlockHeight);
Transaction t = new Transaction(params);
if (prevOut != null) {
// Entirely invalid scriptPubKey to ensure we aren't pre-verifying too much

View File

@@ -35,6 +35,7 @@ import static org.bitcoinj.testing.FakeTxBuilder.createFakeTx;
import static org.junit.Assert.*;
public class ParseByteCacheTest {
private static final int BLOCK_HEIGHT_GENESIS = 0;
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode(
"f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" +
@@ -101,7 +102,7 @@ public class ParseByteCacheTest {
Transaction tx2 = createFakeTx(unitTestParams, COIN,
new ECKey().toAddress(unitTestParams));
Block b1 = createFakeBlock(blockStore, tx1, tx2).block;
Block b1 = createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS, tx1, tx2).block;
MessageSerializer bs = unitTestParams.getDefaultSerializer();

View File

@@ -44,6 +44,8 @@ import static org.junit.Assert.*;
@RunWith(value = Parameterized.class)
public class PeerGroupTest extends TestWithPeerGroup {
private static final int BLOCK_HEIGHT_GENESIS = 0;
private BlockingQueue<Peer> connectedPeers;
private BlockingQueue<Peer> disconnectedPeers;
private AbstractPeerEventListener listener;
@@ -274,7 +276,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
// Set up a little block chain. We heard about b1 but not b2 (it is pending download). b3 is solved whilst we
// are downloading the chain.
Block b1 = FakeTxBuilder.createFakeBlock(blockStore).block;
Block b1 = FakeTxBuilder.createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1);
Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2);
@@ -314,7 +316,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
InboundMessageQueuer p1 = connectPeer(1);
// Set up a little block chain.
Block b1 = FakeTxBuilder.createFakeBlock(blockStore).block;
Block b1 = FakeTxBuilder.createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS).block;
Block b2 = FakeTxBuilder.makeSolvedTestBlock(b1);
Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2);

View File

@@ -123,7 +123,7 @@ public class PeerTest extends TestWithNetworkConnections {
@Test
public void chainDownloadEnd2End() throws Exception {
// A full end-to-end test of the chain download process, with a new block being solved in the middle.
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = makeSolvedTestBlock(b1);
Block b3 = makeSolvedTestBlock(b2);
@@ -199,7 +199,7 @@ public class PeerTest extends TestWithNetworkConnections {
public void invTickle() throws Exception {
connect();
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
// Make a missing block.
Block b2 = makeSolvedTestBlock(b1);
@@ -229,7 +229,7 @@ public class PeerTest extends TestWithNetworkConnections {
connect();
// Make a missing block that we receive.
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = makeSolvedTestBlock(b1);
@@ -305,7 +305,7 @@ public class PeerTest extends TestWithNetworkConnections {
// Check that inventory message containing blocks we want is processed correctly.
@Test
public void newBlock() throws Exception {
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
final Block b2 = makeSolvedTestBlock(b1);
// Receive notification of a new block.
@@ -365,7 +365,7 @@ public class PeerTest extends TestWithNetworkConnections {
// Check that it starts downloading the block chain correctly on request.
@Test
public void startBlockChainDownload() throws Exception {
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = makeSolvedTestBlock(b1);
blockChain.add(b2);
@@ -395,7 +395,7 @@ public class PeerTest extends TestWithNetworkConnections {
public void getBlock() throws Exception {
connect();
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = makeSolvedTestBlock(b1);
Block b3 = makeSolvedTestBlock(b2);
@@ -417,7 +417,7 @@ public class PeerTest extends TestWithNetworkConnections {
public void getLargeBlock() throws Exception {
connect();
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Block b2 = makeSolvedTestBlock(b1);
Transaction t = new Transaction(params);
@@ -444,7 +444,7 @@ public class PeerTest extends TestWithNetworkConnections {
Utils.setMockClock();
// Check that blocks before the fast catchup point are retrieved using getheaders, and after using getblocks.
// This test is INCOMPLETE because it does not check we handle >2000 blocks correctly.
Block b1 = createFakeBlock(blockStore).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
blockChain.add(b1);
Utils.rollMockClock(60 * 10); // 10 minutes later.
Block b2 = makeSolvedTestBlock(b1);

View File

@@ -237,7 +237,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
assertEquals(transactions[0], sendResult.tx);
assertEquals(1, transactions[0].getConfidence().numBroadcastPeers());
// Confirm it.
Block b2 = FakeTxBuilder.createFakeBlock(blockStore, t1).block;
Block b2 = FakeTxBuilder.createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).block;
inbound(p1, b2);
pingAndWait(p1);
assertNull(outbound(p1));

View File

@@ -8,6 +8,8 @@ import org.easymock.*;
import org.junit.*;
import java.util.*;
import static org.bitcoinj.core.BlockTest.params;
import static org.bitcoinj.core.Utils.HEX;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
@@ -27,6 +29,7 @@ public class TransactionTest {
@Before
public void setUp() throws Exception {
Context context = new Context(PARAMS);
dummy = FakeTxBuilder.createFakeTx(PARAMS, Coin.COIN, ADDRESS);
tx = newTransaction();
}
@@ -303,4 +306,17 @@ public class TransactionTest {
tx1.getInput(0).setScriptSig(new Script(new byte[111]));
assertEquals(79, tx1.getMessageSizeForPriorityCalc());
}
/**
*
* @throws VerificationException
*/
@Test
public void testCoinbaseHeightCheck() throws VerificationException {
// Coinbase transaction from block 300,000
final byte[] transactionBytes = HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4803e09304062f503253482f0403c86d53087ceca141295a00002e522cfabe6d6d7561cf262313da1144026c8f7a43e3899c44f6145f39a36507d36679a8b7006104000000000000000000000001c8704095000000001976a91480ad90d403581fa3bf46086a91b2d9d4125db6c188ac00000000");
final int height = 300000;
final Transaction transaction = params.getDefaultSerializer().makeTransaction(transactionBytes);
transaction.checkCoinBaseHeight(height);
}
}

View File

@@ -451,7 +451,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t3);
assertTrue(wallet.isConsistent());
// t2 and t3 gets confirmed in the same block.
BlockPair bp = createFakeBlock(blockStore, t2, t3);
BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t2, t3);
wallet.receiveFromBlock(t2, bp.storedBlock, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0);
wallet.receiveFromBlock(t3, bp.storedBlock, AbstractBlockChain.NewBlockType.BEST_CHAIN, 1);
wallet.notifyNewBestBlock(bp.storedBlock);
@@ -539,7 +539,7 @@ public class WalletTest extends TestWithWallet {
wallet.getBalance(Wallet.BalanceType.ESTIMATED)));
// Now confirm the transaction by including it into a block.
StoredBlock b3 = createFakeBlock(blockStore, spend).storedBlock;
StoredBlock b3 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, spend).storedBlock;
wallet.receiveFromBlock(spend, b3, BlockChain.NewBlockType.BEST_CHAIN, 0);
// Change is confirmed. We started with 5.50 so we should have 4.50 left.
@@ -615,7 +615,7 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(send2, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(Coin.valueOf(0, 80), wallet.getBalance());
Threading.waitForUserCode();
BlockPair b4 = createFakeBlock(blockStore);
BlockPair b4 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS);
confTxns.clear();
wallet.notifyNewBestBlock(b4.storedBlock);
Threading.waitForUserCode();
@@ -916,7 +916,7 @@ public class WalletTest extends TestWithWallet {
assertEquals(TransactionConfidence.ConfidenceType.PENDING,
notifiedTx[0].getConfidence().getConfidenceType());
// Send a block with nothing interesting. Verify we don't get a callback.
wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock);
wallet.notifyNewBestBlock(createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).storedBlock);
Threading.waitForUserCode();
assertNull(reasons[0]);
final Transaction t1Copy = params.getDefaultSerializer().makeTransaction(t1.bitcoinSerialize());
@@ -1151,7 +1151,7 @@ public class WalletTest extends TestWithWallet {
// TX should have been seen as relevant.
assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
assertEquals(ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
Block b1 = createFakeBlock(blockStore, t1).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).block;
chain.add(b1);
// TX should have been seen as relevant, extracted and processed.
assertEquals(value, wallet.getBalance(Wallet.BalanceType.AVAILABLE));
@@ -1234,7 +1234,7 @@ public class WalletTest extends TestWithWallet {
Address watchedAddress = key.toAddress(params);
wallet.addWatchedAddress(watchedAddress);
Transaction t1 = createFakeTx(params, CENT, watchedAddress);
StoredBlock b3 = createFakeBlock(blockStore, t1).storedBlock;
StoredBlock b3 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).storedBlock;
wallet.receiveFromBlock(t1, b3, BlockChain.NewBlockType.BEST_CHAIN, 0);
assertEquals(CENT, wallet.getBalance());
@@ -1255,7 +1255,7 @@ public class WalletTest extends TestWithWallet {
Transaction t1 = createFakeTx(params, CENT, watchedAddress);
Transaction t2 = createFakeTx(params, COIN, notMyAddr);
StoredBlock b1 = createFakeBlock(blockStore, t1).storedBlock;
StoredBlock b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).storedBlock;
Transaction st2 = new Transaction(params);
st2.addOutput(CENT, notMyAddr);
st2.addOutput(COIN, notMyAddr);
@@ -1278,7 +1278,7 @@ public class WalletTest extends TestWithWallet {
assertTrue(wallet.isRequiringUpdateAllBloomFilter());
Transaction t1 = createFakeTx(params, CENT, watchedAddress);
StoredBlock b1 = createFakeBlock(blockStore, t1).storedBlock;
StoredBlock b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).storedBlock;
TransactionOutPoint outPoint = new TransactionOutPoint(params, 0, t1);
@@ -1338,7 +1338,7 @@ public class WalletTest extends TestWithWallet {
for (Address addr : addressesForRemoval) {
Transaction t1 = createFakeTx(params, CENT, addr);
StoredBlock b1 = createFakeBlock(blockStore, t1).storedBlock;
StoredBlock b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).storedBlock;
TransactionOutPoint outPoint = new TransactionOutPoint(params, 0, t1);
@@ -1358,7 +1358,7 @@ public class WalletTest extends TestWithWallet {
assertTrue(wallet.getBloomFilter(0.001).contains(address.getHash160()));
Transaction t1 = createFakeTx(params, CENT, address);
StoredBlock b1 = createFakeBlock(blockStore, t1).storedBlock;
StoredBlock b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).storedBlock;
TransactionOutPoint outPoint = new TransactionOutPoint(params, 0, t1);
@@ -1416,7 +1416,7 @@ public class WalletTest extends TestWithWallet {
assertEquals(f, results[1]);
results[0] = results[1] = null;
Block b0 = createFakeBlock(blockStore).block;
Block b0 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).block;
chain.add(b0);
Sha256Hash hash3 = Sha256Hash.of(f);
assertEquals(hash2, hash3); // File has NOT changed yet. Just new blocks with no txns - delayed.
@@ -1424,7 +1424,7 @@ public class WalletTest extends TestWithWallet {
assertNull(results[1]);
Transaction t1 = createFakeTx(params, valueOf(5, 0), key);
Block b1 = createFakeBlock(blockStore, t1).block;
Block b1 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t1).block;
chain.add(b1);
Sha256Hash hash4 = Sha256Hash.of(f);
assertFalse(hash3.equals(hash4)); // File HAS changed.
@@ -1451,7 +1451,7 @@ public class WalletTest extends TestWithWallet {
wallet.importKey(key2);
assertEquals(hash5, Sha256Hash.of(f)); // File has NOT changed.
Transaction t2 = createFakeTx(params, valueOf(5, 0), key2);
Block b3 = createFakeBlock(blockStore, t2).block;
Block b3 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, t2).block;
chain.add(b3);
Thread.sleep(2000); // Wait longer than autosave delay. TODO Fix the racyness.
assertEquals(hash5, Sha256Hash.of(f)); // File has still NOT changed.
@@ -1511,7 +1511,7 @@ public class WalletTest extends TestWithWallet {
// Add a change address to ensure this tx is relevant.
tx2.addOutput(CENT, wallet.getChangeAddress());
wallet.receivePending(tx2, null);
BlockPair bp = createFakeBlock(blockStore, tx1);
BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, tx1);
wallet.receiveFromBlock(tx1, bp.storedBlock, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0);
wallet.notifyNewBestBlock(bp.storedBlock);
assertEquals(ZERO, wallet.getBalance());
@@ -2981,7 +2981,7 @@ public class WalletTest extends TestWithWallet {
assertEquals(Coin.ZERO, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
assertTrue(bool.get());
// Confirm it in the same manner as how Bloom filtered blocks do. Verify it shows up.
StoredBlock block = createFakeBlock(blockStore, tx).storedBlock;
StoredBlock block = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, tx).storedBlock;
wallet.notifyTransactionIsInBlock(tx.getHash(), block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 1);
assertEquals(COIN, wallet.getBalance());
}
@@ -2989,7 +2989,7 @@ public class WalletTest extends TestWithWallet {
@Test
public void transactionInBlockNotification() {
final Transaction tx = createFakeTx(params, COIN, myAddress);
StoredBlock block = createFakeBlock(blockStore, tx).storedBlock;
StoredBlock block = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, tx).storedBlock;
wallet.receivePending(tx, null);
boolean notification = wallet.notifyTransactionIsInBlock(tx.getHash(), block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 1);
assertTrue(notification);
@@ -2997,7 +2997,7 @@ public class WalletTest extends TestWithWallet {
Address notMyAddr = new ECKey().toAddress(params);
final Transaction tx2 = createFakeTx(params, COIN, notMyAddr);
wallet.receivePending(tx2, null);
StoredBlock block2 = createFakeBlock(blockStore, tx2).storedBlock;
StoredBlock block2 = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS + 1, tx2).storedBlock;
boolean notification2 = wallet.notifyTransactionIsInBlock(tx2.getHash(), block2, AbstractBlockChain.NewBlockType.BEST_CHAIN, 1);
assertFalse(notification2);
}
@@ -3005,7 +3005,7 @@ public class WalletTest extends TestWithWallet {
@Test
public void duplicatedBlock() {
final Transaction tx = createFakeTx(params, COIN, myAddress);
StoredBlock block = createFakeBlock(blockStore, tx).storedBlock;
StoredBlock block = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, tx).storedBlock;
wallet.notifyNewBestBlock(block);
wallet.notifyNewBestBlock(block);
}

View File

@@ -226,9 +226,9 @@ public class ChannelConnectionTest extends TestWithWallet {
// Now confirm the settle TX and see if the channel deletes itself from the wallet.
assertEquals(1, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size());
wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock);
wallet.notifyNewBestBlock(createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).storedBlock);
assertEquals(1, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size());
wallet.notifyNewBestBlock(createFakeBlock(blockStore).storedBlock);
wallet.notifyNewBestBlock(createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS + 1).storedBlock);
assertEquals(0, StoredPaymentChannelClientStates.getFromWallet(wallet).mapChannels.size());
}

View File

@@ -323,7 +323,7 @@ public class WalletProtobufSerializerTest {
@Test
public void coinbaseTxns() throws Exception {
// Covers issue 420 where the outpoint index of a coinbase tx input was being mis-serialized.
Block b = params.getGenesisBlock().createNextBlockWithCoinbase(myKey.getPubKey(), FIFTY_COINS);
Block b = params.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, myKey.getPubKey(), FIFTY_COINS, Block.BLOCK_HEIGHT_GENESIS);
Transaction coinbase = b.getTransactions().get(0);
assertTrue(coinbase.isCoinBase());
BlockChain chain = new BlockChain(params, myWallet, new MemoryBlockStore(params));

View File

@@ -50,10 +50,10 @@ public class VersionTallyTest {
public void testNullWhileEmpty() {
VersionTally instance = new VersionTally(unitTestParams);
for (int i = 0; i < unitTestParams.getMajorityWindow(); i++) {
assertNull(instance.getCount(1));
assertNull(instance.getCountAtOrAbove(1));
instance.add(1);
}
assertEquals(unitTestParams.getMajorityWindow(), instance.getCount(1).intValue());
assertEquals(unitTestParams.getMajorityWindow(), instance.getCountAtOrAbove(1).intValue());
}
/**
@@ -74,16 +74,24 @@ public class VersionTallyTest {
// Fill the tally with 1s
for (int i = 0; i < unitTestParams.getMajorityWindow(); i++) {
assertNull(instance.getCount(1));
assertNull(instance.getCountAtOrAbove(1));
instance.add(1);
}
assertEquals(unitTestParams.getMajorityWindow(), instance.getCount(1).intValue());
assertEquals(unitTestParams.getMajorityWindow(), instance.getCountAtOrAbove(1).intValue());
// Check the count updates as we replace with 2s
for (int i = 0; i < unitTestParams.getMajorityWindow(); i++) {
assertEquals(unitTestParams.getMajorityWindow() - i, instance.getCount(1).intValue());
assertEquals(i, instance.getCount(2).intValue());
assertEquals(i, instance.getCountAtOrAbove(2).intValue());
instance.add(2);
}
// Inject a rogue 1
instance.add(1);
assertEquals(unitTestParams.getMajorityWindow() - 1, instance.getCountAtOrAbove(2).intValue());
// Check we accept high values as well
instance.add(10);
assertEquals(unitTestParams.getMajorityWindow() - 1, instance.getCountAtOrAbove(2).intValue());
}
@Test
@@ -94,14 +102,14 @@ public class VersionTallyTest {
// Build a historical chain of version 2 blocks
long timeSeconds = 1231006505;
StoredBlock chainHead = null;
for (int blockCount = 0; blockCount < unitTestParams.getMajorityWindow(); blockCount++) {
chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds).storedBlock;
for (int height = 0; height < unitTestParams.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds, height).storedBlock;
assertEquals(2, chainHead.getHeader().getVersion());
timeSeconds += 60;
}
VersionTally instance = new VersionTally(unitTestParams);
instance.initialize(blockStore, chainHead);
assertEquals(unitTestParams.getMajorityWindow(), instance.getCount(2).intValue());
assertEquals(unitTestParams.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue());
}
}

View File

@@ -92,7 +92,7 @@ public class DefaultCoinSelectorTest extends TestWithWallet {
// and t3=0.01.
Transaction t1 = checkNotNull(sendMoneyToWallet(COIN, AbstractBlockChain.NewBlockType.BEST_CHAIN));
// Padding block.
wallet.notifyNewBestBlock(FakeTxBuilder.createFakeBlock(blockStore).storedBlock);
wallet.notifyNewBestBlock(FakeTxBuilder.createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS).storedBlock);
final Coin TWO_COINS = COIN.multiply(2);
Transaction t2 = checkNotNull(sendMoneyToWallet(TWO_COINS, AbstractBlockChain.NewBlockType.BEST_CHAIN));
Transaction t3 = checkNotNull(sendMoneyToWallet(CENT, AbstractBlockChain.NewBlockType.BEST_CHAIN));