Fix Sha256Hash hashcode function.

Delete wallet deserialization unit test, as we now consciously are breaking serialization compatibility. Resolves issue 213.
This commit is contained in:
Mike Hearn 2012-07-09 22:28:29 +02:00
parent ec096993e6
commit 9d46c48db9
3 changed files with 3 additions and 82 deletions

View file

@ -31,42 +31,9 @@ import static com.google.common.base.Preconditions.checkArgument;
* map. It also checks that the length is correct and provides a bit more type safety.
*/
public class Sha256Hash implements Serializable {
private static final long serialVersionUID = 3778897922647016546L;
private byte[] bytes;
private int hash = -1;
/**
* @see setHashcodeByteLength(int hashcodeByteLength)
*/
private static int HASHCODE_BYTES_TO_CHECK = 5;
private static boolean HASHCODE_BYTES_TO_CHECK_CHANGED = false;
public static final Sha256Hash ZERO_HASH = new Sha256Hash(new byte[32]);
/**
* Alters the number of bytes from the backing array to use when generating java hashCodes.
* <br/><br/>
* The default value of 5 gives approximately 1 trillion possible unique combinations.
* Given that an int hashcode only has 4 billion possible values it should be more than enough.
* <br/><br/>
* Changing this value after Sha256Hashes have been stored in hashed collections breaks the
* hashCode contract and will result in unpredictable behaviour. If this
* needs to be set to a different value it should be done once and only once
* and before any calls to hashCode() are made on any instance of Sha256Hash instances.
* <br/>
*
* @param hashcodeByteLength the number of bytes in the hash to use for generating the hashcode.
* @throws IllegalStateException if called more than once.
*/
public static void setHashcodeByteLength(int hashcodeByteLength) {
if (HASHCODE_BYTES_TO_CHECK_CHANGED)
throw new IllegalStateException("setHashcodeByteLength can only be called once and should be called before any instances of Sha256Hash are constructed");
HASHCODE_BYTES_TO_CHECK = hashcodeByteLength;
HASHCODE_BYTES_TO_CHECK_CHANGED = true;
}
/**
* Creates a Sha256Hash by wrapping the given byte array. It must be 32 bytes long.
*/
@ -76,12 +43,6 @@ public class Sha256Hash implements Serializable {
}
private Sha256Hash(byte[] bytes, int hash) {
checkArgument(bytes.length == 32);
this.bytes = bytes;
this.hash = hash;
}
/**
* Creates a Sha256Hash by decoding the given hex string. It must be 64 characters long.
*/
@ -118,12 +79,8 @@ public class Sha256Hash implements Serializable {
*/
@Override
public int hashCode() {
if (hash == -1) {
hash = 1;
for (int i = 0; i < HASHCODE_BYTES_TO_CHECK; i++)
hash = 31 * hash + bytes[i];
}
return hash;
// Use the last 4 bytes, not the first 4 which are often zeros in Bitcoin.
return (bytes[31] & 0xFF) | ((bytes[30] & 0xFF) << 8) | ((bytes[29] & 0xFF) << 16) | ((bytes[28] & 0xFF) << 24);
}
@Override
@ -143,6 +100,6 @@ public class Sha256Hash implements Serializable {
}
public Sha256Hash duplicate() {
return new Sha256Hash(bytes, hash);
return new Sha256Hash(bytes);
}
}

View file

@ -19,18 +19,13 @@ package com.google.bitcoin.core;
import com.google.bitcoin.core.WalletTransaction.Pool;
import com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.MemoryBlockStore;
import com.google.bitcoin.store.WalletProtobufSerializer;
import com.google.bitcoin.utils.BriefLogFormatter;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.google.bitcoin.core.TestUtils.*;
import static com.google.bitcoin.core.Utils.bitcoinValueToFriendlyString;
@ -622,37 +617,6 @@ public class WalletTest {
assertNull(tx1.appearsIn);
}
@Test
public void oldWalletsDeserialize() throws Exception {
// Check that the Wallet class fills out tx confidences as best it can when loading old wallets. The new
// API provides a superset of the info that used to be available so it's impossible to do a complete
// migration but we can do some.
//
// TODO: This test does not check migration of dead or pending transactions.
InputStream stream = getClass().getResourceAsStream("old1.wallet");
wallet = Wallet.loadFromFileStream(stream);
Set<Transaction> transactions = wallet.getTransactions(true, true);
assertEquals(91, transactions.size());
Transaction tx = wallet.unspent.get(new Sha256Hash("5649c63ad55002ce2f39d1d4744996ebaccc1d15e0491c9e8d60eb3720dabebd"));
assertEquals(tx.getAppearsInHashes().iterator().next(), new Sha256Hash("00000000019380f5aef28393827737f55a1cf8abb51a36d46ab6f2db0a5b9cb8"));
assertEquals(TransactionConfidence.ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType());
assertEquals(42814, tx.getConfidence().getAppearedAtChainHeight());
// Re-serialize the wallet. Make sure it's all still there.
ByteArrayOutputStream bios = new ByteArrayOutputStream();
wallet.saveToFileStream(bios);
wallet = Wallet.loadFromFileStream(new ByteArrayInputStream(bios.toByteArray()));
assertEquals(91, transactions.size());
tx = wallet.unspent.get(new Sha256Hash("5649c63ad55002ce2f39d1d4744996ebaccc1d15e0491c9e8d60eb3720dabebd"));
assertEquals(tx.getAppearsInHashes().iterator().next(), new Sha256Hash("00000000019380f5aef28393827737f55a1cf8abb51a36d46ab6f2db0a5b9cb8"));
assertEquals(TransactionConfidence.ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType());
assertEquals(42814, tx.getConfidence().getAppearedAtChainHeight());
// Now check we can serialize old wallets to protocol buffers. Covers bug 134.
bios.reset();
new WalletProtobufSerializer().writeWallet(wallet, bios);
}
@Test
public void spendToSameWallet() throws Exception {
// Test that a spend to the same wallet is dealt with correctly.