diff --git a/docs/com/google/bitcoin/core/Block.html b/docs/com/google/bitcoin/core/Block.html index cc4922d9..b33355dd 100644 --- a/docs/com/google/bitcoin/core/Block.html +++ b/docs/com/google/bitcoin/core/Block.html @@ -73,9 +73,9 @@ function windowTitle() - SUMMARY: NESTED | FIELD | CONSTR | METHOD + SUMMARY: NESTED | FIELD | CONSTR | METHOD -DETAIL: FIELD | CONSTR | METHOD +DETAIL: FIELD | CONSTR | METHOD @@ -124,14 +124,6 @@ A block is the foundation of the BitCoin system. It records a set of Field Summary - - -static long -ALLOWED_TIME_DRIFT - -
-            -   @@ -186,6 +178,22 @@ A block is the foundation of the BitCoin system. It records a set of + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -255,25 +344,6 @@ A block is the foundation of the BitCoin system. It records a set of -
+ longgetDifficultyTarget() + +
+          Returns the difficulty of the proof of work that this block should meet encoded in compact form.
+ java.math.BigIntegergetDifficultyTargetBI() + +
+          Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash.
 byte[] getHash() @@ -211,9 +219,90 @@ A block is the foundation of the BitCoin system. It records a set of + longgetNonce() + +
+          Returns the nonce, an arbitrary value that exists only to make the hash of the block header fall below the + difficulty target.
+ byte[]getPrevBlockHash() + +
+          Returns the hash of the previous block in the chain, as defined by the block header.
+ longgetTime() + +
+          Returns the time at which the block was solved and broadcast, according to the clock of the solving node.
+ longgetVersion() + +
+          Returns the version of the block data structure as defined by the BitCoin protocol.
 int hashCode() +
+           
+ voidsetDifficultyTarget(long compactForm) + +
+           
+ voidsetMerkleRoot(byte[] value) + +
+           
+ voidsetNonce(long nonce) + +
+           
+ voidsetPrevBlockHash(byte[] prevBlockHash) + +
+           
+ voidsetTime(long time) + +
+           
+ voidsetVersion(long version) +
           
- - - -
-Field Detail
- -

-ALLOWED_TIME_DRIFT

-
-public static final long ALLOWED_TIME_DRIFT
-
-
-
See Also:
Constant Field Values
-
- @@ -414,6 +484,17 @@ public byte[] getMerkleRoot()
+

+setMerkleRoot

+
+public void setMerkleRoot(byte[] value)
+
+
+
+
+
+
+

addTransaction

@@ -425,6 +506,142 @@ public void addTransaction(

+getVersion

+
+public long getVersion()
+
+
Returns the version of the block data structure as defined by the BitCoin protocol. +

+

+
+
+
+
+ +

+setVersion

+
+public void setVersion(long version)
+
+
+
+
+
+
+ +

+getPrevBlockHash

+
+public byte[] getPrevBlockHash()
+
+
Returns the hash of the previous block in the chain, as defined by the block header. +

+

+
+
+
+
+ +

+setPrevBlockHash

+
+public void setPrevBlockHash(byte[] prevBlockHash)
+
+
+
+
+
+
+ +

+getTime

+
+public long getTime()
+
+
Returns the time at which the block was solved and broadcast, according to the clock of the solving node. +

+

+
+
+
+
+ +

+setTime

+
+public void setTime(long time)
+
+
+
+
+
+
+ +

+getDifficultyTarget

+
+public long getDifficultyTarget()
+
+
Returns the difficulty of the proof of work that this block should meet encoded in compact form. The + BlockChain verifies that this is not too easy by looking at the length of the chain when the block is + added. To find the actual value the hash should be compared against, use getDifficultyTargetBI. +

+

+
+
+
+
+ +

+getDifficultyTargetBI

+
+public java.math.BigInteger getDifficultyTargetBI()
+
+
Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash. +

+

+
+
+
+
+ +

+setDifficultyTarget

+
+public void setDifficultyTarget(long compactForm)
+
+
+
+
+
+
+ +

+getNonce

+
+public long getNonce()
+
+
Returns the nonce, an arbitrary value that exists only to make the hash of the block header fall below the + difficulty target. +

+

+
+
+
+
+ +

+setNonce

+
+public void setNonce(long nonce)
+
+
+
+
+

@@ -476,9 +693,9 @@ public void addTransaction( - SUMMARY: NESTED | FIELD | CONSTR | METHOD + SUMMARY: NESTED | FIELD | CONSTR | METHOD -DETAIL: FIELD | CONSTR | METHOD +DETAIL: FIELD | CONSTR | METHOD diff --git a/docs/com/google/bitcoin/core/NetworkConnection.html b/docs/com/google/bitcoin/core/NetworkConnection.html index f5622071..d5792e03 100644 --- a/docs/com/google/bitcoin/core/NetworkConnection.html +++ b/docs/com/google/bitcoin/core/NetworkConnection.html @@ -162,6 +162,14 @@ A NetworkConnection handles talking to a remote BitCoin peer at a low level. It + java.lang.String +toString() + +
+            + + +  void writeMessage(java.lang.String tag, Message message) @@ -176,7 +184,7 @@ A NetworkConnection handles talking to a remote BitCoin peer at a low level. It Methods inherited from class java.lang.Object -clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait +clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait   @@ -253,19 +261,35 @@ public void shutdown()
+

+toString

+
+public java.lang.String toString()
+
+
+
Overrides:
toString in class java.lang.Object
+
+
+
+
+
+
+

readMessage

 public Message readMessage()
-                    throws ProtocolException
+ throws java.io.IOException, + ProtocolException
Reads a network message from the wire, blocking until the message is fully received.

-
Returns:
An instance of a Message subclass. +
Returns:
An instance of a Message subclass
Throws: -
ProtocolException - if the message is badly formatted, failed checksum or there was a protocol failure.
+
ProtocolException - if the message is badly formatted, failed checksum or there was a TCP failure. +
java.io.IOException

diff --git a/docs/constant-values.html b/docs/constant-values.html index f6bd916b..cef3b8e4 100644 --- a/docs/constant-values.html +++ b/docs/constant-values.html @@ -95,24 +95,6 @@ com.google.*

- - - - - - - - - - - -
com.google.bitcoin.core.Block
-public static final longALLOWED_TIME_DRIFT7200L
- -

- -

- diff --git a/docs/index-all.html b/docs/index-all.html index 6f98fc12..99b512f7 100644 --- a/docs/index-all.html +++ b/docs/index-all.html @@ -112,9 +112,6 @@ Variable in class com.google.bitcoin.core.AddressMessage - Class in com.google.bitcoin.core
 
addTransaction(Transaction) - Method in class com.google.bitcoin.core.Block
Adds a transaction to this block. -
ALLOWED_TIME_DRIFT - -Static variable in class com.google.bitcoin.core.Block -
 

@@ -272,6 +269,12 @@ Constructor for class com.google.bitcoin.core.GetDataMessage - Class in com.google.bitcoin.core
 
GetDataMessage(NetworkParameters, byte[]) - Constructor for class com.google.bitcoin.core.GetDataMessage
  +
getDifficultyTarget() - +Method in class com.google.bitcoin.core.Block +
Returns the difficulty of the proof of work that this block should meet encoded in compact form. +
getDifficultyTargetBI() - +Method in class com.google.bitcoin.core.Block +
Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash.
getFromAddress() - Method in class com.google.bitcoin.core.Script
Convenience wrapper around getPubKey. @@ -300,6 +303,13 @@ Method in class com.google.bitcoin.core.getMerkleRoot() - Method in class com.google.bitcoin.core.Block
Returns the merkle root in big endian form, calculating it from transactions if necessary. +
getNonce() - +Method in class com.google.bitcoin.core.Block +
Returns the nonce, an arbitrary value that exists only to make the hash of the block header fall below the + difficulty target. +
getPrevBlockHash() - +Method in class com.google.bitcoin.core.Block +
Returns the hash of the previous block in the chain, as defined by the block header.
getPubKey() - Method in class com.google.bitcoin.core.ECKey
Gets the raw public key value. @@ -325,6 +335,9 @@ Method in class com.google.bitcoin.core.getSizeInBytes() - Method in class com.google.bitcoin.core.VarInt
  +
getTime() - +Method in class com.google.bitcoin.core.Block +
Returns the time at which the block was solved and broadcast, according to the clock of the solving node.
getToAddress() - Method in class com.google.bitcoin.core.Script
Gets the destination address from this script, if it's in the required form (see getPubKey). @@ -340,6 +353,9 @@ Method in class com.google.bitcoin.core.getValueSentToMe(Wallet) - Method in class com.google.bitcoin.core.Transaction
Returns the sum of the outputs that are sending coins to a key in our wallet. +
getVersion() - +Method in class com.google.bitcoin.core.Block +
Returns the version of the block data structure as defined by the BitCoin protocol.

@@ -594,9 +610,27 @@ Constructor for exception com.google.bitcoin.core.sendCoins(Peer, Address, BigInteger) - Method in class com.google.bitcoin.core.Wallet
Sends coins to the given address, via the given Peer. +
setDifficultyTarget(long) - +Method in class com.google.bitcoin.core.Block +
  +
setMerkleRoot(byte[]) - +Method in class com.google.bitcoin.core.Block +
  +
setNonce(long) - +Method in class com.google.bitcoin.core.Block +
  +
setPrevBlockHash(byte[]) - +Method in class com.google.bitcoin.core.Block +
  +
setTime(long) - +Method in class com.google.bitcoin.core.Block +
 
setTracing(boolean) - Method in class com.google.bitcoin.core.Script
If true, running a program will log its instructions. +
setVersion(long) - +Method in class com.google.bitcoin.core.Block +
 
sha256hash160(byte[]) - Static method in class com.google.bitcoin.core.Utils
Calculates RIPEMD160(SHA256(input)). @@ -650,6 +684,9 @@ Method in class com.google.bitcoin.core.toString() - Method in class com.google.bitcoin.core.InventoryItem
  +
toString() - +Method in class com.google.bitcoin.core.NetworkConnection +
 
toString() - Method in class com.google.bitcoin.core.PeerAddress
  diff --git a/docs/serialized-form.html b/docs/serialized-form.html index 3d3e48b0..7fa49946 100644 --- a/docs/serialized-form.html +++ b/docs/serialized-form.html @@ -204,6 +204,8 @@ transactions

 java.util.List<E> transactions
+
If null, it means this object holds only the headers. +

@@ -213,6 +215,8 @@ hash

 byte[] hash
+
Stores the hash of the block. If null, getHash() will recalculate it. +

diff --git a/src/com/google/bitcoin/core/Block.java b/src/com/google/bitcoin/core/Block.java index e4af58cd..a244486a 100644 --- a/src/com/google/bitcoin/core/Block.java +++ b/src/com/google/bitcoin/core/Block.java @@ -33,18 +33,22 @@ import static com.google.bitcoin.core.Utils.*; * you grab it from a downloaded {@link BlockChain}. */ public class Block extends Message { - public static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as official client. + static final long ALLOWED_TIME_DRIFT = 2 * 60 * 60; // Same value as official client. - long version; - byte[] prevBlockHash; - byte[] merkleRoot; - long time; - long difficultyTarget; // "nBits" - long nonce; + /** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */ + static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL; - // If null, it means this object holds only the headers. + private long version; + private byte[] prevBlockHash; + private byte[] merkleRoot; + private long time; + private long difficultyTarget; // "nBits" + private long nonce; + + /** If null, it means this object holds only the headers. */ List transactions; - byte[] hash; + /** Stores the hash of the block. If null, getHash() will recalculate it. */ + private byte[] hash; // If set, points towards the previous block in the chain. Note that a block may have multiple other blocks // pointing back to it because despite being called a "chain", the block chain is in fact a tree. There can be @@ -106,7 +110,9 @@ public class Block extends Message { } } - /** Returns hash in little endian form */ + /** + * Calculates the block hash by serializing the block and hashing the resulting bytes. + */ private byte[] calculateHash() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -155,8 +161,29 @@ public class Block extends Message { } return s.toString(); } - - private void checkProofOfWork() throws VerificationException { + + /** + * Finds a value of nonce that makes the blocks hash lower than the difficulty target. This is called mining, + * but solve() is far too slow to do real mining with. It exists only for unit testing purposes and is not a part + * of the public API. + * + * This can loop forever if a solution cannot be found solely by incrementing nonce. It doesn't change extraNonce. + */ + void solve() { + while (true) { + try { + // Is our proof of work valid yet? + if (checkProofOfWork(false)) return; + // No, so increment the nonce and try again. + setNonce(getNonce() + 1); + } catch (VerificationException e) { + throw new RuntimeException(e); // Cannot happen. + } + } + } + + /** Returns true if the hash of the block is OK (lower than difficulty target). */ + private boolean checkProofOfWork(boolean throwException) throws VerificationException { // This part is key - it is what proves the block was as difficult to make as it claims // to be. Note however that in the context of this function, the block can claim to be // as difficult as it wants to be .... if somebody was able to take control of our network @@ -170,11 +197,16 @@ public class Block extends Message { if (target.compareTo(BigInteger.valueOf(0)) <= 0 || target.compareTo(params.proofOfWorkLimit) > 0) throw new VerificationException("Difficulty target is bad"); - byte[] hashBytes = (hash == null ? calculateHash() : hash); - BigInteger h = new BigInteger(1, hashBytes); - if (h.compareTo(target) > 0) - throw new VerificationException("Hash is higher than target: " + bytesToHexString(hashBytes) + " vs " + - target.toString(16)); + BigInteger h = new BigInteger(1, getHash()); + if (h.compareTo(target) > 0) { + // Proof of work check failed! + if (throwException) + throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs " + + target.toString(16)); + else + return false; + } + return true; } private void checkTimestamp() throws VerificationException { @@ -260,7 +292,7 @@ public class Block extends Message { // // 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(); + checkProofOfWork(true); checkTimestamp(); // 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 @@ -281,14 +313,12 @@ public class Block extends Message { if (hash != null && other.hash != null) return Arrays.equals(hash, other.hash); // Otherwise we have to do it the slow way. - byte[] me = bitcoinSerialize(); - byte[] them = other.bitcoinSerialize(); - return Arrays.equals(me, them); + return Arrays.equals(getHash(), other.getHash()); } @Override public int hashCode() { - return Arrays.hashCode(hash); + return Arrays.hashCode(getHash()); } /** Returns the merkle root in big endian form, calculating it from transactions if necessary. */ @@ -298,6 +328,11 @@ public class Block extends Message { return merkleRoot; } + public void setMerkleRoot(byte[] value) { + merkleRoot = value; + hash = null; + } + /** * Adds a transaction to this block. */ @@ -310,4 +345,82 @@ public class Block extends Message { merkleRoot = null; hash = null; } + + /** + * Returns the version of the block data structure as defined by the BitCoin protocol. + */ + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + this.hash = null; + } + + /** + * Returns the hash of the previous block in the chain, as defined by the block header. + */ + public byte[] getPrevBlockHash() { + return prevBlockHash; + } + + public void setPrevBlockHash(byte[] prevBlockHash) { + this.prevBlockHash = prevBlockHash; + this.hash = null; + } + + /** + * Returns the time at which the block was solved and broadcast, according to the clock of the solving node. + */ + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + this.hash = null; + } + + /** + * Returns the difficulty of the proof of work that this block should meet encoded in compact form. The + * {@link BlockChain} verifies that this is not too easy by looking at the length of the chain when the block is + * added. To find the actual value the hash should be compared against, use getDifficultyTargetBI. + */ + public long getDifficultyTarget() { + return difficultyTarget; + } + + /** + * Returns the difficulty target as a 256 bit value that can be compared to a SHA-256 hash. + */ + public BigInteger getDifficultyTargetBI() { + return Utils.decodeCompactBits(getDifficultyTarget()); + } + + public void setDifficultyTarget(long compactForm) { + this.difficultyTarget = compactForm; + this.hash = null; + } + + /** + * Returns the nonce, an arbitrary value that exists only to make the hash of the block header fall below the + * difficulty target. + */ + public long getNonce() { + return nonce; + } + + public void setNonce(long nonce) { + this.nonce = nonce; + this.hash = null; + } + + /** Adds a fake coinbase transaction for unit tests. */ + void addFakeTransaction() { + transactions = new ArrayList(); + Transaction coinbase = new Transaction(params); + coinbase.setFakeHashForTesting(Utils.doubleDigest("test tx".getBytes())); + transactions.add(coinbase); + } } diff --git a/src/com/google/bitcoin/core/BlockChain.java b/src/com/google/bitcoin/core/BlockChain.java index 889fefab..d7d3255c 100644 --- a/src/com/google/bitcoin/core/BlockChain.java +++ b/src/com/google/bitcoin/core/BlockChain.java @@ -107,7 +107,7 @@ public class BlockChain { LOG("Re-received block that is currently on top of the chain."); return true; } - if (!Arrays.equals(block.prevBlockHash, prev.getHash())) { + if (!Arrays.equals(block.getPrevBlockHash(), prev.getHash())) { // The block does not fit onto the top of the chain. It can either be: // - Entirely unconnected. This can happen when a new block is solved and broadcast whilst we are in // the process of downloading the block chain. @@ -140,7 +140,7 @@ public class BlockChain { blocksConnectedThisRound = 0; for (int i = 0; i < unconnectedBlocks.size(); i++) { Block block = unconnectedBlocks.get(i); - if (Arrays.equals(block.prevBlockHash, blockChain.getLast().getHash())) { + if (Arrays.equals(block.getPrevBlockHash(), blockChain.getLast().getHash())) { // False here ensures we don't recurse infinitely downwards when connecting huge chains. add(block, false); unconnectedBlocks.remove(i); @@ -163,21 +163,22 @@ public class BlockChain { // Is this supposed to be a difficulty transition point? if (blockChain.size() % INTERVAL != 0) { // No ... so check the difficulty didn't actually change. - if (top.difficultyTarget != prev.difficultyTarget) + if (top.getDifficultyTarget() != prev.getDifficultyTarget()) throw new VerificationException("Unexpected change in difficulty at height " + blockChain.size() + - ": " + Long.toHexString(top.difficultyTarget) + " vs " + Long.toHexString(prev.difficultyTarget)); + ": " + Long.toHexString(top.getDifficultyTarget()) + " vs " + + Long.toHexString(prev.getDifficultyTarget())); return; } Block blockIntervalAgo = blockChain.get(blockChain.size() - INTERVAL); - int timespan = (int) (prev.time - blockIntervalAgo.time); + int timespan = (int) (prev.getTime() - blockIntervalAgo.getTime()); // Limit the adjustment step. if (timespan < TARGET_TIMESPAN / 4) timespan = TARGET_TIMESPAN / 4; if (timespan > TARGET_TIMESPAN * 4) timespan = TARGET_TIMESPAN * 4; - BigInteger newDifficulty = Utils.decodeCompactBits(blockIntervalAgo.difficultyTarget); + BigInteger newDifficulty = Utils.decodeCompactBits(blockIntervalAgo.getDifficultyTarget()); newDifficulty = newDifficulty.multiply(BigInteger.valueOf(timespan)); newDifficulty = newDifficulty.divide(BigInteger.valueOf(TARGET_TIMESPAN)); @@ -185,8 +186,8 @@ public class BlockChain { newDifficulty = params.proofOfWorkLimit; } - int accuracyBytes = (int) (top.difficultyTarget >>> 24) - 3; - BigInteger receivedDifficulty = Utils.decodeCompactBits(top.difficultyTarget); + int accuracyBytes = (int) (top.getDifficultyTarget() >>> 24) - 3; + BigInteger receivedDifficulty = top.getDifficultyTargetBI(); // The calculated difficulty is to a higher precision than received, so reduce here. BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8); diff --git a/src/com/google/bitcoin/core/NetworkParameters.java b/src/com/google/bitcoin/core/NetworkParameters.java index cd752628..88d20f40 100644 --- a/src/com/google/bitcoin/core/NetworkParameters.java +++ b/src/com/google/bitcoin/core/NetworkParameters.java @@ -78,9 +78,9 @@ public class NetworkParameters implements Serializable { n.port = 18333; n.addressHeader = 111; n.genesisBlock = createGenesis(n); - n.genesisBlock.time = 1296688602L; - n.genesisBlock.difficultyTarget = 0x1d07fff8L; - n.genesisBlock.nonce = 384568319; + n.genesisBlock.setTime(1296688602L); + n.genesisBlock.setDifficultyTarget(0x1d07fff8L); + n.genesisBlock.setNonce(384568319); String genesisHash = n.genesisBlock.getHashAsString(); assert genesisHash.equals("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); return n; @@ -94,11 +94,19 @@ public class NetworkParameters implements Serializable { n.packetMagic = 0xf9beb4d9L; n.addressHeader = 0; n.genesisBlock = createGenesis(n); - n.genesisBlock.difficultyTarget = 0x1d00ffffL; - n.genesisBlock.time = 1231006505; - n.genesisBlock.nonce = 2083236893; + n.genesisBlock.setDifficultyTarget(0x1d00ffffL); + n.genesisBlock.setTime(1231006505L); + n.genesisBlock.setNonce(2083236893); String genesisHash = n.genesisBlock.getHashAsString(); - assert genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); + assert genesisHash.equals("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") : genesisHash; + return n; + } + + /** Returns a testnet params modified to allow any difficulty target. */ + static NetworkParameters unitTests() { + NetworkParameters n = NetworkParameters.testNet(); + n.proofOfWorkLimit = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); + n.genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); return n; } } diff --git a/src/com/google/bitcoin/core/Peer.java b/src/com/google/bitcoin/core/Peer.java index 546de5c3..bade7e2e 100644 --- a/src/com/google/bitcoin/core/Peer.java +++ b/src/com/google/bitcoin/core/Peer.java @@ -98,7 +98,6 @@ public class Peer { LOG("Shutting down peer thread"); } else { // We caught an unexpected exception. - System.err.println(e.toString()); e.printStackTrace(); } } diff --git a/tests/com/google/bitcoin/core/BlockChainTest.java b/tests/com/google/bitcoin/core/BlockChainTest.java index e68f55de..cbe3607f 100644 --- a/tests/com/google/bitcoin/core/BlockChainTest.java +++ b/tests/com/google/bitcoin/core/BlockChainTest.java @@ -24,6 +24,7 @@ import java.math.BigInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; // Tests still to write: // - Rest of checkDifficultyTransitions: verify we don't accept invalid transitions. @@ -50,18 +51,13 @@ public class BlockChainTest { Block b2 = getBlock2(); // Let's try adding an invalid block. - long n = b2.nonce; + long n = b2.getNonce(); try { - // TODO: Encapsulate these fields behind setters that recalc the hash automatically. - b2.nonce = 12345; - b2.hash = null; + b2.setNonce(12345); chain.add(b2); - // We should not get here. - assertTrue(false); + fail(); } catch (VerificationException e) { - // Pass. - b2.nonce = n; - b2.hash = null; + b2.setNonce(n); } assertTrue(chain.add(b2)); } @@ -74,20 +70,20 @@ public class BlockChainTest { NetworkParameters params2 = NetworkParameters.testNet(); Block bad = new Block(params2); // Merkle root can be anything here, doesn't matter. - bad.merkleRoot = Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + bad.setMerkleRoot(Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); // Nonce was just some number that made the hash < difficulty limit set below, it can be anything. - bad.nonce = 140548933; - bad.time = 1279242649; - bad.prevBlockHash = b2.getHash(); + bad.setNonce(140548933); + bad.setTime(1279242649); + bad.setPrevBlockHash(b2.getHash()); // We're going to make this block so easy 50% of solutions will pass, and check it gets rejected for having a // bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all // solutions. - bad.difficultyTarget = 0x207fFFFFL; + bad.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); try { chain.add(bad); // The difficulty target above should be rejected on the grounds of being easier than the networks // allowable difficulty. - assertTrue(false); + fail(); } catch (VerificationException e) { assertTrue(e.getMessage(), e.getMessage().indexOf("Difficulty target is bad") >= 0); } @@ -98,7 +94,7 @@ public class BlockChainTest { try { chain.add(bad); // We should not get here as the difficulty target should not be changing at this point. - assertTrue(false); + fail(); } catch (VerificationException e) { assertTrue(e.getMessage(), e.getMessage().indexOf("Unexpected change in difficulty") >= 0); } @@ -108,11 +104,10 @@ public class BlockChainTest { private Block getBlock2() throws Exception { Block b2 = new Block(params); - b2.merkleRoot = Hex.decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"); - b2.nonce = 2642058077L; - b2.time = 1296734343; - b2.prevBlockHash = - Hex.decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"); + b2.setMerkleRoot(Hex.decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b")); + b2.setNonce(2642058077L); + b2.setTime(1296734343L); + b2.setPrevBlockHash(Hex.decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")); assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString()); b2.verify(); return b2; @@ -120,10 +115,10 @@ public class BlockChainTest { private Block getBlock1() throws Exception { Block b1 = new Block(params); - b1.merkleRoot = Hex.decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"); - b1.nonce = 236038445; - b1.time = 1296734340; - b1.prevBlockHash = Hex.decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"); + b1.setMerkleRoot(Hex.decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10")); + b1.setNonce(236038445); + b1.setTime(1296734340); + b1.setPrevBlockHash(Hex.decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")); assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString()); b1.verify(); return b1; diff --git a/tests/com/google/bitcoin/core/BlockTest.java b/tests/com/google/bitcoin/core/BlockTest.java index 1c62b296..cea00544 100644 --- a/tests/com/google/bitcoin/core/BlockTest.java +++ b/tests/com/google/bitcoin/core/BlockTest.java @@ -25,13 +25,10 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class BlockTest { static final NetworkParameters params = NetworkParameters.testNet(); - static final byte[] blockBytes; static { @@ -44,6 +41,38 @@ public class BlockTest { public void testBlockVerification() throws Exception { Block block = new Block(params, blockBytes); block.verify(); + assertEquals("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.getHashAsString()); + } + + @Test + public void testProofOfWork() throws Exception { + // This params accepts any difficulty target. + NetworkParameters params = NetworkParameters.unitTests(); + Block block = new Block(params, blockBytes); + block.setNonce(12346); + try { + block.verify(); + fail(); + } catch (VerificationException e) { + // Expected. + } + // Blocks contain their own difficulty target. The BlockChain verification mechanism is what stops real blocks + // from containing artificially weak difficulties. + block.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET); + // Now it should pass. + block.verify(); + // Break the nonce again at the lower difficulty level so we can try solving for it. + block.setNonce(1); + try { + block.verify(); + fail(); + } catch (VerificationException e) { + // Expected to fail as the nonce is no longer correct. + } + // Should find an acceptable nonce. + block.solve(); + block.verify(); + assertEquals(block.getNonce(), 2); } @Test @@ -56,7 +85,7 @@ public class BlockTest { block.transactions.set(1, tx1); try { block.verify(); - assertTrue("Verified when should not.", false); + fail(); } catch (VerificationException e) { // We should get here. }
com.google.bitcoin.core.Message