Mark as spent the outputs used by pending transactions. Resolves issue 243.

This commit is contained in:
Mike Hearn 2012-08-20 14:42:16 +02:00
parent c4304fe07d
commit 9f036bff84
3 changed files with 54 additions and 3 deletions

View File

@ -13,4 +13,5 @@ Steve Coughlan <shadders.del@gmail.com>
Roman Mandeleil <roman.mandeleil@gmail.com>
Chris Rico <chrisrico@gmail.com>
Vasile Rotaru <vrotaru.md@gmail.com>
Joseph Gleason <fireduck@gmail.com>
Joseph Gleason <fireduck@gmail.com>
Wilhelmus Petrus van Cuijk <phedny@gmail.com>

View File

@ -866,8 +866,12 @@ public class Wallet implements Serializable {
// Not found in the unspent map. Try again with the spent map.
result = input.connect(spent, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) {
// Doesn't spend any of our outputs or is coinbase.
continue;
// Not found in the unspent and spent maps. Try again with the pending map.
result = input.connect(pending, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
if (result == TransactionInput.ConnectionResult.NO_SUCH_TX) {
// Doesn't spend any of our outputs or is coinbase.
continue;
}
}
}

View File

@ -16,6 +16,7 @@
package com.google.bitcoin.core;
import com.google.bitcoin.core.Transaction.SigHash;
import com.google.bitcoin.core.WalletTransaction.Pool;
import com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.MemoryBlockStore;
@ -789,6 +790,51 @@ public class WalletTest {
assertNotNull(results[0]);
assertEquals(f, results[1]);
}
@Test
public void spendOutputFromPendingTransaction() throws Exception {
// We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change.
BigInteger v1 = Utils.toNanoCoins(1, 0);
Transaction t1 = createFakeTx(params, v1, myAddress);
wallet.receiveFromBlock(t1, null, BlockChain.NewBlockType.BEST_CHAIN);
assertEquals(v1, wallet.getBalance());
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.ALL));
// First create our current transaction
ECKey k2 = new ECKey();
wallet.addKey(k2);
BigInteger v2 = toNanoCoins(0, 50);
Transaction t2 = new Transaction(params);
TransactionOutput o2 = new TransactionOutput(params, t2, v2, k2.toAddress(params));
t2.addOutput(o2);
boolean complete = wallet.completeTx(t2);
assertTrue(complete);
// Commit t2, so it is placed in the pending pool
wallet.commitTx(t2);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.ALL));
// Now try the spend the output
ECKey k3 = new ECKey();
BigInteger v3 = toNanoCoins(0, 25);
Transaction t3 = new Transaction(params);
t3.addOutput(v3, k3.toAddress(params));
t3.addInput(o2);
t3.signInputs(SigHash.ALL, wallet);
// Commit t3, so the coins from the pending t2 are spent
wallet.commitTx(t3);
assertEquals(0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT));
assertEquals(2, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
assertEquals(3, wallet.getPoolSize(WalletTransaction.Pool.ALL));
// Now the output of t2 must not be available for spending
assertFalse(o2.isAvailableForSpending());
}
// There is a test for spending a coinbase transaction as it matures in BlockChainTest#coinbaseTransactionAvailability