From 9f036bff84f1066940a9fe1f843f739535a58310 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 20 Aug 2012 14:42:16 +0200 Subject: [PATCH] Mark as spent the outputs used by pending transactions. Resolves issue 243. --- AUTHORS | 3 +- .../java/com/google/bitcoin/core/Wallet.java | 8 +++- .../com/google/bitcoin/core/WalletTest.java | 46 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 453f6a8b..845ca15f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,4 +13,5 @@ Steve Coughlan Roman Mandeleil Chris Rico Vasile Rotaru -Joseph Gleason \ No newline at end of file +Joseph Gleason +Wilhelmus Petrus van Cuijk \ No newline at end of file diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index f376cdcf..943a2a91 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -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; + } } } diff --git a/core/src/test/java/com/google/bitcoin/core/WalletTest.java b/core/src/test/java/com/google/bitcoin/core/WalletTest.java index 220bd0a3..67236db4 100644 --- a/core/src/test/java/com/google/bitcoin/core/WalletTest.java +++ b/core/src/test/java/com/google/bitcoin/core/WalletTest.java @@ -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