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

View File

@ -221,7 +221,7 @@ public class WalletProtobufSerializer {
Transaction tx = wtx.getTransaction();
Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder();
txBuilder.setPool(Protos.Transaction.Pool.valueOf(wtx.getPool().getValue()))
txBuilder.setPool(getProtoPool(wtx))
.setHash(hashToByteString(tx.getHash()))
.setVersion((int) tx.getVersion());
@ -288,6 +288,17 @@ public class WalletProtobufSerializer {
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,
TransactionConfidence confidence,
Protos.TransactionConfidence.Builder confidenceBuilder) {
@ -351,19 +362,15 @@ public class WalletProtobufSerializer {
* @throws UnreadableWalletException thrown in various error conditions (see description).
*/
public Wallet readWallet(InputStream input) throws UnreadableWalletException {
Protos.Wallet walletProto = null;
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) {
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 {
Transaction tx = txMap.get(txProto.getHash());
WalletTransaction.Pool pool = WalletTransaction.Pool.valueOf(txProto.getPool().getNumber());
if (pool == WalletTransaction.Pool.INACTIVE || pool == WalletTransaction.Pool.PENDING_INACTIVE) {
final WalletTransaction.Pool pool;
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.
// 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
// 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++) {
TransactionOutput output = tx.getOutputs().get(i);

View File

@ -16,60 +16,22 @@
package com.google.bitcoin.wallet;
import com.google.bitcoin.core.Transaction;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A Transaction in a Wallet - includes the pool ID
*
* @author Miron Cuperman
* Stores data about a transaction that is only relevant to the {@link com.google.bitcoin.core.Wallet} class.
*/
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 {
UNSPENT(4), // unspent in best chain
SPENT(5), // spent in best chain
INACTIVE(2), // in alt chain
DEAD(10), // double-spend in alt 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;
}
}
UNSPENT, // unspent in best chain
SPENT, // spent in best chain
DEAD, // double-spend in alt chain
PENDING, // a pending tx we would like to go into the best chain
}
private Transaction transaction;
private Pool pool;
private final Transaction transaction;
private final Pool pool;
public WalletTransaction(Pool pool, Transaction transaction) {
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("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).
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 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).
req = Wallet.SendRequest.to(destination, v2);
@ -191,7 +191,7 @@ public class WalletTest extends TestWithWallet {
Transaction t2 = req.tx;
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(Transaction.Purpose.USER_PAYMENT, t2.getPurpose());
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 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 ALL pool size", 1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
assertEquals("Incorrect confirmed tx ALL pool size", 1, wallet.getTransactions(true).size());
Threading.waitForUserCode();
assertTrue(availFuture.isDone());
assertTrue(estimatedFuture.isDone());
@ -263,7 +263,7 @@ public class WalletTest extends TestWithWallet {
Threading.waitForUserCode();
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
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(1, txns.size());
}
@ -296,7 +296,7 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(v1, wallet.getBalance());
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
assertEquals(1, wallet.getTransactions(true).size());
ECKey k2 = new ECKey();
Address a2 = k2.toAddress(params);
@ -321,7 +321,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t2);
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL));
assertEquals(2, wallet.getTransactions(true).size());
}
@Test
@ -332,11 +332,11 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(v1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
assertEquals(v1, wallet.getBalance());
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);
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.add(v2), wallet.getBalance(Wallet.BalanceType.ESTIMATED));
}
@ -347,7 +347,7 @@ public class WalletTest extends TestWithWallet {
BigInteger v1 = toNanoCoins(5, 0);
BigInteger v2 = toNanoCoins(0, 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);
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
sendMoneyToWallet(v2, AbstractBlockChain.NewBlockType.BEST_CHAIN);
@ -543,7 +543,7 @@ public class WalletTest extends TestWithWallet {
// in the unspent pool, not pending or spent.
BigInteger coinHalf = Utils.toNanoCoins(0, 50);
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);
Transaction outbound1 = wallet.createSend(someOtherGuy, coinHalf);
wallet.commitTx(outbound1);
@ -899,7 +899,7 @@ public class WalletTest extends TestWithWallet {
sendMoneyToWallet(coin1, AbstractBlockChain.NewBlockType.BEST_CHAIN);
// 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.ALL));
assertEquals(1, wallet.getTransactions(true).size());
Transaction outbound1 = wallet.createSend(myAddress, coinHalf);
wallet.commitTx(outbound1);
// We should have a zero available balance before the next block.
@ -1134,7 +1134,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t2);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
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.
ECKey k3 = new ECKey();
@ -1148,7 +1148,7 @@ public class WalletTest extends TestWithWallet {
wallet.commitTx(t3);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
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
assertFalse(o2.isAvailableForSpending());