mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-22 14:22:45 +01:00
Block: make verify*()
static helper methods
This commit is contained in:
parent
cf8715eb4b
commit
ac2e244bc8
4 changed files with 76 additions and 70 deletions
|
@ -497,7 +497,7 @@ public abstract class AbstractBlockChain {
|
|||
// are only lightly verified: presence in a valid connecting block is taken as proof of validity. See the
|
||||
// article here for more details: https://bitcoinj.github.io/security-model
|
||||
try {
|
||||
block.verifyHeader();
|
||||
Block.verifyHeader(block);
|
||||
storedPrev = getStoredBlockInCurrentScope(block.getPrevBlockHash());
|
||||
if (storedPrev != null) {
|
||||
height = storedPrev.getHeight() + 1;
|
||||
|
@ -506,7 +506,7 @@ public abstract class AbstractBlockChain {
|
|||
}
|
||||
flags = params.getBlockVerificationFlags(block, versionTally, height);
|
||||
if (shouldVerifyTransactions())
|
||||
block.verifyTransactions(height, flags);
|
||||
Block.verifyTransactions(params, block, height, flags);
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify block: ", e);
|
||||
log.error(block.getHashAsString());
|
||||
|
|
|
@ -645,62 +645,6 @@ public class Block extends Message {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the block data to ensure it follows the rules laid out in the network parameters. Specifically,
|
||||
* throws an exception if the proof of work is invalid, or if the timestamp is too far from what it should be.
|
||||
* This is <b>not</b> everything that is required for a block to be valid, only what is checkable independent
|
||||
* of the chain and without a transaction index.
|
||||
*
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public void verifyHeader() throws VerificationException {
|
||||
// Prove that this block is OK. It might seem that we can just ignore most of these checks given that the
|
||||
// network is also verifying the blocks, but we cannot as it'd open us to a variety of obscure attacks.
|
||||
//
|
||||
// Firstly we need to ensure this block does in fact represent real work done. If the difficulty is high
|
||||
// enough, it's probably been done by the network.
|
||||
checkProofOfWork(true);
|
||||
checkTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the block contents
|
||||
*
|
||||
* @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(final int height, final EnumSet<VerifyFlag> 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
|
||||
// transactions that reference spent or non-existent inputs.
|
||||
if (transactions.isEmpty())
|
||||
throw new VerificationException("Block had no transactions");
|
||||
if (this.getMessageSize() > MAX_BLOCK_SIZE)
|
||||
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
||||
checkTransactions(height, flags);
|
||||
checkMerkleRoot();
|
||||
checkSigOps();
|
||||
for (Transaction tx : transactions)
|
||||
Transaction.verify(params, tx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(final int height, final EnumSet<VerifyFlag> flags) throws VerificationException {
|
||||
verifyHeader();
|
||||
verifyTransactions(height, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -943,7 +887,7 @@ public class Block extends Message {
|
|||
b.setTime(bitcoinTime);
|
||||
b.solve();
|
||||
try {
|
||||
b.verifyHeader();
|
||||
Block.verifyHeader(b);
|
||||
} catch (VerificationException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
|
@ -1026,4 +970,66 @@ public class Block extends Message {
|
|||
public boolean isBIP65() {
|
||||
return version >= BLOCK_VERSION_BIP65;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies both the header and that the transactions hash to the merkle root.
|
||||
*
|
||||
* @param params parameters for the verification rules
|
||||
* @param block block to verify
|
||||
* @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 at least one of the rules is violated
|
||||
*/
|
||||
public static void verify(NetworkParameters params, Block block, int height, EnumSet<VerifyFlag> flags) throws VerificationException {
|
||||
verifyHeader(block);
|
||||
verifyTransactions(params, block, height, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the block data to ensure it follows the rules laid out in the network parameters. Specifically,
|
||||
* throws an exception if the proof of work is invalid, or if the timestamp is too far from what it should be.
|
||||
* This is <b>not</b> everything that is required for a block to be valid, only what is checkable independent
|
||||
* of the chain and without a transaction index.
|
||||
*
|
||||
* @param block block to verify
|
||||
* @throws VerificationException if at least one of the rules is violated
|
||||
*/
|
||||
public static void verifyHeader(Block block) throws VerificationException {
|
||||
// Prove that this block is OK. It might seem that we can just ignore most of these checks given that the
|
||||
// network is also verifying the blocks, but we cannot as it'd open us to a variety of obscure attacks.
|
||||
//
|
||||
// Firstly we need to ensure this block does in fact represent real work done. If the difficulty is high
|
||||
// enough, it's probably been done by the network.
|
||||
block.checkProofOfWork(true);
|
||||
block.checkTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the block contents
|
||||
*
|
||||
* @param params parameters for the verification rules
|
||||
* @param block block to verify
|
||||
* @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 at least one of the rules is violated
|
||||
*/
|
||||
public static void verifyTransactions(NetworkParameters params, Block block, int height,
|
||||
EnumSet<VerifyFlag> 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
|
||||
// transactions that reference spent or non-existent inputs.
|
||||
if (block.transactions.isEmpty())
|
||||
throw new VerificationException("Block had no transactions");
|
||||
if (block.getMessageSize() > MAX_BLOCK_SIZE)
|
||||
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
||||
block.checkTransactions(height, flags);
|
||||
block.checkMerkleRoot();
|
||||
block.checkSigOps();
|
||||
for (Transaction tx : block.transactions)
|
||||
Transaction.verify(params, tx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -402,7 +402,7 @@ public class BlockChainTest {
|
|||
b2.setDifficultyTarget(0x1d00ffff);
|
||||
b2.setPrevBlockHash(Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"));
|
||||
assertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashAsString());
|
||||
b2.verifyHeader();
|
||||
Block.verifyHeader(b2);
|
||||
return b2;
|
||||
}
|
||||
|
||||
|
@ -414,7 +414,7 @@ public class BlockChainTest {
|
|||
b1.setDifficultyTarget(0x1d00ffff);
|
||||
b1.setPrevBlockHash(Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
|
||||
assertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashAsString());
|
||||
b1.verifyHeader();
|
||||
Block.verifyHeader(b1);
|
||||
return b1;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ public class BlockTest {
|
|||
|
||||
@Test
|
||||
public void testBlockVerification() {
|
||||
block700000.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TESTNET, block700000, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,7 +103,7 @@ public class BlockTest {
|
|||
Block block = TWEAK_TESTNET.getDefaultSerializer().makeBlock(ByteBuffer.wrap(block700000Bytes));
|
||||
block.setNonce(12346);
|
||||
try {
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TWEAK_TESTNET, block, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// Expected.
|
||||
|
@ -112,18 +112,18 @@ public class BlockTest {
|
|||
// from containing artificially weak difficulties.
|
||||
block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
|
||||
// Now it should pass.
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TWEAK_TESTNET, block, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
// Break the nonce again at the lower difficulty level so we can try solving for it.
|
||||
block.setNonce(1);
|
||||
try {
|
||||
block.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TWEAK_TESTNET, block, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.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.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TWEAK_TESTNET, block, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -134,7 +134,7 @@ public class BlockTest {
|
|||
block700000.transactions.set(0, tx2);
|
||||
block700000.transactions.set(1, tx1);
|
||||
try {
|
||||
block700000.verify(Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(TESTNET, block700000, Block.BLOCK_HEIGHT_GENESIS, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
fail();
|
||||
} catch (VerificationException e) {
|
||||
// We should get here.
|
||||
|
@ -168,7 +168,7 @@ public class BlockTest {
|
|||
|
||||
// Check block.
|
||||
assertEquals("0000000004053156021d8e42459d284220a7f6e087bf78f30179c3703ca4eefa", block.getHashAsString());
|
||||
block.verify(21066, EnumSet.of(Block.VerifyFlag.HEIGHT_IN_COINBASE));
|
||||
Block.verify(TESTNET, block, 21066, EnumSet.of(Block.VerifyFlag.HEIGHT_IN_COINBASE));
|
||||
|
||||
// Testnet block 32768 (hash 000000007590ba495b58338a5806c2b6f10af921a70dbd814e0da3c6957c0c03)
|
||||
// contains a coinbase transaction whose height is three bytes, but could
|
||||
|
@ -180,7 +180,7 @@ public class BlockTest {
|
|||
|
||||
// Check block.
|
||||
assertEquals("000000007590ba495b58338a5806c2b6f10af921a70dbd814e0da3c6957c0c03", block.getHashAsString());
|
||||
block.verify(32768, EnumSet.of(Block.VerifyFlag.HEIGHT_IN_COINBASE));
|
||||
Block.verify(TESTNET, block, 32768, EnumSet.of(Block.VerifyFlag.HEIGHT_IN_COINBASE));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -199,7 +199,7 @@ public class BlockTest {
|
|||
|
||||
// Check block.
|
||||
assertNotNull(block169482);
|
||||
block169482.verify(169482, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
Block.verify(MAINNET, block169482, 169482, EnumSet.noneOf(Block.VerifyFlag.class));
|
||||
assertEquals(BLOCK_NONCE, block169482.getNonce());
|
||||
|
||||
StoredBlock storedBlock = new StoredBlock(block169482, BigInteger.ONE, 169482); // Nonsense work - not used in test.
|
||||
|
|
Loading…
Add table
Reference in a new issue