3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-15 03:35:52 +00:00

Clean up WalletTransaction and serialization code a bit.

This commit is contained in:
Mike Hearn 2013-12-22 18:57:13 +01:00
parent b3673999d4
commit f318808cf7
4 changed files with 51 additions and 78 deletions

View File

@ -1333,9 +1333,6 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
case DEAD: case DEAD:
checkState(dead.put(tx.getHash(), tx) == null); checkState(dead.put(tx.getHash(), tx) == null);
break; break;
case PENDING_INACTIVE:
checkState(pending.put(tx.getHash(), tx) == null);
break;
default: default:
throw new RuntimeException("Unknown wallet transaction type " + pool); throw new RuntimeException("Unknown wallet transaction type " + pool);
} }
@ -1459,8 +1456,6 @@ public class Wallet implements Serializable, BlockChainListener, PeerFilterProvi
return pending.size(); return pending.size();
case DEAD: case DEAD:
return dead.size(); return dead.size();
case ALL:
return unspent.size() + spent.size() + pending.size() + dead.size();
} }
throw new RuntimeException("Unreachable"); throw new RuntimeException("Unreachable");
} finally { } finally {

View File

@ -221,7 +221,7 @@ public class WalletProtobufSerializer {
Transaction tx = wtx.getTransaction(); Transaction tx = wtx.getTransaction();
Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder(); Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder();
txBuilder.setPool(Protos.Transaction.Pool.valueOf(wtx.getPool().getValue())) txBuilder.setPool(getProtoPool(wtx))
.setHash(hashToByteString(tx.getHash())) .setHash(hashToByteString(tx.getHash()))
.setVersion((int) tx.getVersion()); .setVersion((int) tx.getVersion());
@ -288,6 +288,17 @@ public class WalletProtobufSerializer {
return txBuilder.build(); return txBuilder.build();
} }
private static Protos.Transaction.Pool getProtoPool(WalletTransaction wtx) {
switch (wtx.getPool()) {
case UNSPENT: return Protos.Transaction.Pool.UNSPENT;
case SPENT: return Protos.Transaction.Pool.SPENT;
case DEAD: return Protos.Transaction.Pool.DEAD;
case PENDING: return Protos.Transaction.Pool.PENDING;
default:
throw new RuntimeException("Unreachable");
}
}
private static void writeConfidence(Protos.Transaction.Builder txBuilder, private static void writeConfidence(Protos.Transaction.Builder txBuilder,
TransactionConfidence confidence, TransactionConfidence confidence,
Protos.TransactionConfidence.Builder confidenceBuilder) { Protos.TransactionConfidence.Builder confidenceBuilder) {
@ -351,19 +362,15 @@ public class WalletProtobufSerializer {
* @throws UnreadableWalletException thrown in various error conditions (see description). * @throws UnreadableWalletException thrown in various error conditions (see description).
*/ */
public Wallet readWallet(InputStream input) throws UnreadableWalletException { public Wallet readWallet(InputStream input) throws UnreadableWalletException {
Protos.Wallet walletProto = null;
try { try {
walletProto = parseToProto(input); Protos.Wallet walletProto = parseToProto(input);
NetworkParameters params = NetworkParameters.fromID(walletProto.getNetworkIdentifier());
Wallet wallet = new Wallet(params);
readWallet(walletProto, wallet);
return wallet;
} catch (IOException e) { } catch (IOException e) {
throw new UnreadableWalletException("Could not load wallet file", e); throw new UnreadableWalletException("Could not parse input stream to protobuf", e);
} }
// System.out.println(TextFormat.printToString(walletProto));
NetworkParameters params = NetworkParameters.fromID(walletProto.getNetworkIdentifier());
Wallet wallet = new Wallet(params);
readWallet(walletProto, wallet);
return wallet;
} }
/** /**
@ -566,13 +573,22 @@ public class WalletProtobufSerializer {
private WalletTransaction connectTransactionOutputs(org.bitcoinj.wallet.Protos.Transaction txProto) throws UnreadableWalletException { private WalletTransaction connectTransactionOutputs(org.bitcoinj.wallet.Protos.Transaction txProto) throws UnreadableWalletException {
Transaction tx = txMap.get(txProto.getHash()); Transaction tx = txMap.get(txProto.getHash());
WalletTransaction.Pool pool = WalletTransaction.Pool.valueOf(txProto.getPool().getNumber()); final WalletTransaction.Pool pool;
if (pool == WalletTransaction.Pool.INACTIVE || pool == WalletTransaction.Pool.PENDING_INACTIVE) { switch (txProto.getPool()) {
case DEAD: pool = WalletTransaction.Pool.DEAD; break;
case PENDING: pool = WalletTransaction.Pool.PENDING; break;
case SPENT: pool = WalletTransaction.Pool.SPENT; break;
case UNSPENT: pool = WalletTransaction.Pool.UNSPENT; break;
// Upgrade old wallets: inactive pool has been merged with the pending pool. // Upgrade old wallets: inactive pool has been merged with the pending pool.
// Remove this some time after 0.9 is old and everyone has upgraded. // Remove this some time after 0.9 is old and everyone has upgraded.
// There should not be any spent outputs in this tx as old wallets would not allow them to be spent // There should not be any spent outputs in this tx as old wallets would not allow them to be spent
// in this state. // in this state.
pool = WalletTransaction.Pool.PENDING; case INACTIVE:
case PENDING_INACTIVE:
pool = WalletTransaction.Pool.PENDING;
break;
default:
throw new UnreadableWalletException("Unknown transaction pool: " + txProto.getPool());
} }
for (int i = 0 ; i < tx.getOutputs().size() ; i++) { for (int i = 0 ; i < tx.getOutputs().size() ; i++) {
TransactionOutput output = tx.getOutputs().get(i); TransactionOutput output = tx.getOutputs().get(i);

View File

@ -16,60 +16,22 @@
package com.google.bitcoin.wallet; package com.google.bitcoin.wallet;
import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.Transaction;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* A Transaction in a Wallet - includes the pool ID * Stores data about a transaction that is only relevant to the {@link com.google.bitcoin.core.Wallet} class.
*
* @author Miron Cuperman
*/ */
public class WalletTransaction { public class WalletTransaction {
/**
* This is a bitfield oriented enum, with the following bits:
*
* <li>bit 0 - spent
* <li>bit 1 - appears in alt chain
* <li>bit 2 - appears in best chain
* <li>bit 3 - double-spent
* <li>bit 4 - pending (we would like the tx to go into the best chain)
*
* <p>Not all combinations are interesting, just the ones actually used in the enum.
*/
public enum Pool { public enum Pool {
UNSPENT(4), // unspent in best chain UNSPENT, // unspent in best chain
SPENT(5), // spent in best chain SPENT, // spent in best chain
INACTIVE(2), // in alt chain DEAD, // double-spend in alt chain
DEAD(10), // double-spend in alt chain PENDING, // a pending tx we would like to go into the best chain
PENDING(16), // a pending tx we would like to go into the best chain
PENDING_INACTIVE(18), // a pending tx in alt but not in best yet
ALL(-1);
private int value;
Pool(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Pool valueOf(int value) {
switch (value) {
case 4: return UNSPENT;
case 5: return SPENT;
case 2: return INACTIVE;
case 10: return DEAD;
case 16: return PENDING;
case 18: return PENDING_INACTIVE;
default: return null;
}
}
} }
private Transaction transaction; private final Transaction transaction;
private Pool pool; private final Pool pool;
public WalletTransaction(Pool pool, Transaction transaction) { public WalletTransaction(Pool pool, Transaction transaction) {
this.pool = checkNotNull(pool); this.pool = checkNotNull(pool);

View File

@ -161,7 +161,7 @@ public class WalletTest extends TestWithWallet {
assertEquals("This ECKey is encrypted but no decryption key has been supplied.", kce.getMessage()); assertEquals("This ECKey is encrypted but no decryption key has been supplied.", kce.getMessage());
} }
assertEquals("Wrong number of UNSPENT.1", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of UNSPENT.1", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals("Wrong number of ALL.1", 1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals("Wrong number of ALL.1", 1, wallet.getTransactions(true).size());
// Try to create a send with a fee but the wrong password (this should fail). // Try to create a send with a fee but the wrong password (this should fail).
req = Wallet.SendRequest.to(destination, v2); req = Wallet.SendRequest.to(destination, v2);
@ -177,7 +177,7 @@ public class WalletTest extends TestWithWallet {
} }
assertEquals("Wrong number of UNSPENT.2", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of UNSPENT.2", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals("Wrong number of ALL.2", 1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals("Wrong number of ALL.2", 1, wallet.getTransactions(true).size());
// Create a send with a fee with the correct password (this should succeed). // Create a send with a fee with the correct password (this should succeed).
req = Wallet.SendRequest.to(destination, v2); req = Wallet.SendRequest.to(destination, v2);
@ -191,7 +191,7 @@ public class WalletTest extends TestWithWallet {
Transaction t2 = req.tx; Transaction t2 = req.tx;
assertEquals("Wrong number of UNSPENT.3", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of UNSPENT.3", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals("Wrong number of ALL.3", 1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals("Wrong number of ALL.3", 1, wallet.getTransactions(true).size());
assertEquals(TransactionConfidence.Source.SELF, t2.getConfidence().getSource()); assertEquals(TransactionConfidence.Source.SELF, t2.getConfidence().getSource());
assertEquals(Transaction.Purpose.USER_PAYMENT, t2.getPurpose()); assertEquals(Transaction.Purpose.USER_PAYMENT, t2.getPurpose());
assertEquals(wallet.getChangeAddress(), t2.getOutput(1).getScriptPubKey().getToAddress(params)); assertEquals(wallet.getChangeAddress(), t2.getOutput(1).getScriptPubKey().getToAddress(params));
@ -230,7 +230,7 @@ public class WalletTest extends TestWithWallet {
assertEquals("Incorrect confirmed tx balance", v1, wallet.getBalance()); assertEquals("Incorrect confirmed tx balance", v1, wallet.getBalance());
assertEquals("Incorrect confirmed tx PENDING pool size", 0, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals("Incorrect confirmed tx PENDING pool size", 0, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals("Incorrect confirmed tx UNSPENT pool size", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Incorrect confirmed tx UNSPENT pool size", 1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals("Incorrect confirmed tx ALL pool size", 1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals("Incorrect confirmed tx ALL pool size", 1, wallet.getTransactions(true).size());
Threading.waitForUserCode(); Threading.waitForUserCode();
assertTrue(availFuture.isDone()); assertTrue(availFuture.isDone());
assertTrue(estimatedFuture.isDone()); assertTrue(estimatedFuture.isDone());
@ -263,7 +263,7 @@ public class WalletTest extends TestWithWallet {
Threading.waitForUserCode(); Threading.waitForUserCode();
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(2, wallet.getTransactions(true).size());
assertEquals(t, txns.getFirst()); assertEquals(t, txns.getFirst());
assertEquals(1, txns.size()); assertEquals(1, txns.size());
} }
@ -296,7 +296,7 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(v1, wallet.getBalance()); assertEquals(v1, wallet.getBalance());
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(1, wallet.getTransactions(true).size());
ECKey k2 = new ECKey(); ECKey k2 = new ECKey();
Address a2 = k2.toAddress(params); Address a2 = k2.toAddress(params);
@ -321,7 +321,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t2); wallet.commitTx(t2);
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(2, wallet.getTransactions(true).size());
} }
@Test @Test
@ -332,11 +332,11 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(v1, wallet.getBalance()); assertEquals(v1, wallet.getBalance());
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(1, wallet.getTransactions(true).size());
BigInteger v2 = toNanoCoins(0, 50); BigInteger v2 = toNanoCoins(0, 50);
sendMoneyToWallet(v2, AbstractBlockChain.NewBlockType.SIDE_CHAIN); sendMoneyToWallet(v2, AbstractBlockChain.NewBlockType.SIDE_CHAIN);
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(2, wallet.getTransactions(true).size());
assertEquals(v1, wallet.getBalance()); assertEquals(v1, wallet.getBalance());
assertEquals(v1.add(v2), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(v1.add(v2), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
} }
@ -347,7 +347,7 @@ public class WalletTest extends TestWithWallet {
BigInteger v1 = toNanoCoins(5, 0); BigInteger v1 = toNanoCoins(5, 0);
BigInteger v2 = toNanoCoins(0, 50); BigInteger v2 = toNanoCoins(0, 50);
BigInteger expected = toNanoCoins(5, 50); BigInteger expected = toNanoCoins(5, 50);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(0, wallet.getTransactions(true).size());
sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
sendMoneyToWallet(v2, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(v2, AbstractBlockChain.NewBlockType.BEST_CHAIN);
@ -543,7 +543,7 @@ public class WalletTest extends TestWithWallet {
// in the unspent pool, not pending or spent. // in the unspent pool, not pending or spent.
BigInteger coinHalf = Utils.toNanoCoins(0, 50); BigInteger coinHalf = Utils.toNanoCoins(0, 50);
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(1, wallet.getTransactions(true).size());
Address someOtherGuy = new ECKey().toAddress(params); Address someOtherGuy = new ECKey().toAddress(params);
Transaction outbound1 = wallet.createSend(someOtherGuy, coinHalf); Transaction outbound1 = wallet.createSend(someOtherGuy, coinHalf);
wallet.commitTx(outbound1); wallet.commitTx(outbound1);
@ -899,7 +899,7 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(coin1, AbstractBlockChain.NewBlockType.BEST_CHAIN); sendMoneyToWallet(coin1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
// Send half to ourselves. We should then have a balance available to spend of zero. // Send half to ourselves. We should then have a balance available to spend of zero.
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(1, wallet.getTransactions(true).size());
Transaction outbound1 = wallet.createSend(myAddress, coinHalf); Transaction outbound1 = wallet.createSend(myAddress, coinHalf);
wallet.commitTx(outbound1); wallet.commitTx(outbound1);
// We should have a zero available balance before the next block. // We should have a zero available balance before the next block.
@ -1134,7 +1134,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t2); wallet.commitTx(t2);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(2, wallet.getTransactions(true).size());
// Now try to the spend the output. // Now try to the spend the output.
ECKey k3 = new ECKey(); ECKey k3 = new ECKey();
@ -1148,7 +1148,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t3); wallet.commitTx(t3);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(3, wallet.getPoolSize(WalletTransaction.Pool.ALL)); assertEquals(3, wallet.getTransactions(true).size());
// Now the output of t2 must not be available for spending // Now the output of t2 must not be available for spending
assertFalse(o2.isAvailableForSpending()); assertFalse(o2.isAvailableForSpending());