mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 02:05:53 +00:00
Migrate old wallets to use transaction confidences, as much as possible, and add a unit test for deserializing old wallets.
This commit is contained in:
parent
be8d3c3896
commit
7f82613559
@ -22,7 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
import static com.google.bitcoin.core.Utils.*;
|
||||
|
||||
/**
|
||||
@ -56,7 +56,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
|
||||
private long lockTime;
|
||||
|
||||
// This is being migrated to appearsInHashes
|
||||
// This is being migrated to appearsInHashes. It's set to null after migration.
|
||||
Set<StoredBlock> appearsIn;
|
||||
|
||||
// Stored only in Java serialization. This is either the time the transaction was broadcast as measured from the
|
||||
|
@ -320,6 +320,9 @@ public class TransactionConfidence implements Serializable {
|
||||
|
||||
/**
|
||||
* If this transaction has been overridden by a double spend (is dead), this call returns the overriding transaction.
|
||||
* Note that this call <b>can return null</b> if you have migrated an old wallet, as pre-Jan 2012 wallets did not
|
||||
* store this information.
|
||||
*
|
||||
* @return the transaction that double spent this one
|
||||
* @throws IllegalStateException if confidence type is not OVERRIDDEN_BY_DOUBLE_SPEND.
|
||||
*/
|
||||
|
@ -137,6 +137,11 @@ public class Wallet implements Serializable {
|
||||
|
||||
private final NetworkParameters params;
|
||||
|
||||
// Primitive kind of versioning protocol that does not break serializability. If this is true it means the
|
||||
// Transaction objects in this wallet have confidence objects. If false (the default for old wallets missing
|
||||
// this field) then we need to migrate.
|
||||
private boolean hasTransactionConfidences;
|
||||
|
||||
transient private ArrayList<WalletEventListener> eventListeners;
|
||||
|
||||
/**
|
||||
@ -152,6 +157,7 @@ public class Wallet implements Serializable {
|
||||
pending = new HashMap<Sha256Hash, Transaction>();
|
||||
dead = new HashMap<Sha256Hash, Transaction>();
|
||||
eventListeners = new ArrayList<WalletEventListener>();
|
||||
hasTransactionConfidences = true;
|
||||
}
|
||||
|
||||
public NetworkParameters getNetworkParameters() {
|
||||
@ -178,7 +184,7 @@ public class Wallet implements Serializable {
|
||||
/**
|
||||
* Uses Java serialization to save the wallet to the given file stream.
|
||||
*/
|
||||
public synchronized void saveToFileStream(FileOutputStream f) throws IOException {
|
||||
public synchronized void saveToFileStream(OutputStream f) throws IOException {
|
||||
ObjectOutputStream oos = new ObjectOutputStream(f);
|
||||
oos.writeObject(this);
|
||||
oos.close();
|
||||
@ -208,9 +214,9 @@ public class Wallet implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a wallet deserialized from the given file input stream.
|
||||
* Returns a wallet deserialized from the given input stream.
|
||||
*/
|
||||
public static Wallet loadFromFileStream(FileInputStream f) throws IOException {
|
||||
public static Wallet loadFromFileStream(InputStream f) throws IOException {
|
||||
ObjectInputStream ois = null;
|
||||
try {
|
||||
ois = new ObjectInputStream(f);
|
||||
@ -225,6 +231,44 @@ public class Wallet implements Serializable {
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
eventListeners = new ArrayList<WalletEventListener>();
|
||||
maybeMigrateToTransactionConfidences();
|
||||
}
|
||||
|
||||
/** Migrate old wallets that don't have any tx confidences, filling out whatever information we can. */
|
||||
private void maybeMigrateToTransactionConfidences() {
|
||||
if (hasTransactionConfidences) return;
|
||||
// We can't fill out tx confidence objects exactly, we don't have enough data to do that. But we do the
|
||||
// best we can.
|
||||
List<Transaction> transactions = new LinkedList<Transaction>();
|
||||
transactions.addAll(unspent.values());
|
||||
transactions.addAll(spent.values());
|
||||
for (Transaction tx : transactions) {
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
confidence.setConfidenceType(TransactionConfidence.ConfidenceType.BUILDING);
|
||||
Set<StoredBlock> appearsIn = tx.appearsIn;
|
||||
// appearsIn is being migrated away from, in favor of just storing the hashes instead of full blocks.
|
||||
// TODO: Clear this code out once old wallets fade away.
|
||||
if (appearsIn != null) {
|
||||
int minHeight = Integer.MAX_VALUE;
|
||||
for (StoredBlock block : appearsIn) {
|
||||
minHeight = Math.min(minHeight, block.getHeight());
|
||||
}
|
||||
confidence.setAppearedAtChainHeight(minHeight);
|
||||
}
|
||||
}
|
||||
for (Transaction tx : pending.values()) {
|
||||
tx.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN);
|
||||
}
|
||||
for (Transaction tx : inactive.values()) {
|
||||
tx.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.NOT_IN_BEST_CHAIN);
|
||||
}
|
||||
for (Transaction tx : dead.values()) {
|
||||
tx.getConfidence().setConfidenceType(TransactionConfidence.ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND);
|
||||
// We'd ideally like to set overridingTransaction here, but old wallets don't have that data.
|
||||
// Dead transactions in the wallet should be rare, so API users will just have to handle this
|
||||
// edge case until old wallets have gone away.
|
||||
}
|
||||
hasTransactionConfidences = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,9 +22,13 @@ 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.createFakeBlock;
|
||||
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
||||
@ -520,5 +524,32 @@ 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());
|
||||
}
|
||||
|
||||
// Support for offline spending is tested in PeerGroupTest
|
||||
}
|
||||
|
BIN
tests/com/google/bitcoin/core/old1.wallet
Normal file
BIN
tests/com/google/bitcoin/core/old1.wallet
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user