mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-14 11:15:51 +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.io.*;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static com.google.bitcoin.core.Utils.*;
|
import static com.google.bitcoin.core.Utils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,7 +56,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
|
|
||||||
private long lockTime;
|
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;
|
Set<StoredBlock> appearsIn;
|
||||||
|
|
||||||
// Stored only in Java serialization. This is either the time the transaction was broadcast as measured from the
|
// 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.
|
* 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
|
* @return the transaction that double spent this one
|
||||||
* @throws IllegalStateException if confidence type is not OVERRIDDEN_BY_DOUBLE_SPEND.
|
* @throws IllegalStateException if confidence type is not OVERRIDDEN_BY_DOUBLE_SPEND.
|
||||||
*/
|
*/
|
||||||
|
@ -137,6 +137,11 @@ public class Wallet implements Serializable {
|
|||||||
|
|
||||||
private final NetworkParameters params;
|
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;
|
transient private ArrayList<WalletEventListener> eventListeners;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,6 +157,7 @@ public class Wallet implements Serializable {
|
|||||||
pending = new HashMap<Sha256Hash, Transaction>();
|
pending = new HashMap<Sha256Hash, Transaction>();
|
||||||
dead = new HashMap<Sha256Hash, Transaction>();
|
dead = new HashMap<Sha256Hash, Transaction>();
|
||||||
eventListeners = new ArrayList<WalletEventListener>();
|
eventListeners = new ArrayList<WalletEventListener>();
|
||||||
|
hasTransactionConfidences = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkParameters getNetworkParameters() {
|
public NetworkParameters getNetworkParameters() {
|
||||||
@ -178,7 +184,7 @@ public class Wallet implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Uses Java serialization to save the wallet to the given file stream.
|
* 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);
|
ObjectOutputStream oos = new ObjectOutputStream(f);
|
||||||
oos.writeObject(this);
|
oos.writeObject(this);
|
||||||
oos.close();
|
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;
|
ObjectInputStream ois = null;
|
||||||
try {
|
try {
|
||||||
ois = new ObjectInputStream(f);
|
ois = new ObjectInputStream(f);
|
||||||
@ -225,6 +231,44 @@ public class Wallet implements Serializable {
|
|||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
in.defaultReadObject();
|
||||||
eventListeners = new ArrayList<WalletEventListener>();
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.google.bitcoin.core.TestUtils.createFakeBlock;
|
import static com.google.bitcoin.core.TestUtils.createFakeBlock;
|
||||||
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
import static com.google.bitcoin.core.TestUtils.createFakeTx;
|
||||||
@ -520,5 +524,32 @@ public class WalletTest {
|
|||||||
assertNull(tx1.appearsIn);
|
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
|
// 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