mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 02:05:53 +00:00
Don't mark outputs that spend to non-wallet addresses as spent. This avoids a problem in the case where you send somebody coins with change and they immediately send you the coins back. Add a unit test to prove the bug is solved. Existing wallets will need to be refreshed. Resolves issue 64.
This commit is contained in:
parent
3327e02f80
commit
dafb806f05
@ -338,10 +338,13 @@ public class Wallet implements Serializable {
|
||||
// A -> spent by B [pending]
|
||||
// \-> spent by C [chain]
|
||||
Transaction doubleSpent = input.outpoint.fromTx; // == A
|
||||
assert doubleSpent != null;
|
||||
int index = (int) input.outpoint.index;
|
||||
TransactionOutput output = doubleSpent.outputs.get(index);
|
||||
TransactionInput spentBy = output.getSpentBy();
|
||||
assert spentBy != null;
|
||||
Transaction connected = spentBy.parentTransaction;
|
||||
assert connected != null;
|
||||
if (pending.containsKey(connected.getHash())) {
|
||||
log.info("Saw double spend from chain override pending tx {}", connected.getHashAsString());
|
||||
log.info(" <-pending ->dead");
|
||||
@ -404,15 +407,7 @@ public class Wallet implements Serializable {
|
||||
TransactionOutput connectedOutput = input.outpoint.getConnectedOutput();
|
||||
connectedOutput.markAsSpent(input);
|
||||
}
|
||||
// Some of the outputs probably send coins back to us, eg for change or because this transaction is just
|
||||
// consolidating the wallet. Mark any output that is NOT back to us as spent. Then add this TX to the
|
||||
// pending pool.
|
||||
for (TransactionOutput output : tx.outputs) {
|
||||
if (!output.isMine(this)) {
|
||||
// This output didn't go to us, so by definition it is now spent.
|
||||
output.markAsSpent(null);
|
||||
}
|
||||
}
|
||||
// Add to the pending pool. It'll be moved out once we receive this transaction on the best chain.
|
||||
pending.put(tx.getHash(), tx);
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,9 @@ public class WalletTest {
|
||||
Address someOtherGuy = new ECKey().toAddress(params);
|
||||
TransactionOutput output = new TransactionOutput(params, tx, Utils.toNanoCoins(0, 5), someOtherGuy);
|
||||
tx.addOutput(output);
|
||||
// Note that tx is no longer valid: it spends more than it imports. However checking transactions balance
|
||||
// correctly isn't possible in SPV mode because value is a property of outputs not inputs. Without all
|
||||
// transactions you can't check they add up.
|
||||
wallet.receive(tx, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
// Now the other guy creates a transaction which spends that change.
|
||||
Transaction tx2 = new Transaction(params);
|
||||
@ -193,6 +196,29 @@ public class WalletTest {
|
||||
assertEquals(Utils.toNanoCoins(0, 0), tx2.getValueSentFromMe(wallet));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bounce() throws Exception {
|
||||
// This test covers bug 64 (False double spends). Check that if we create a spend and it's immediately sent
|
||||
// back to us, this isn't considered as a double spend.
|
||||
BigInteger coin1 = Utils.toNanoCoins(1, 0);
|
||||
BigInteger coinHalf = Utils.toNanoCoins(0, 50);
|
||||
// Start by giving us 1 coin.
|
||||
Transaction inbound1 = createFakeTx(params, coin1, myAddress);
|
||||
wallet.receive(inbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
// Send half to some other guy. Sending only half then waiting for a confirm is important to ensure the tx is
|
||||
// in the unspent pool, not pending or spent.
|
||||
Address someOtherGuy = new ECKey().toAddress(params);
|
||||
Transaction outbound1 = wallet.createSend(someOtherGuy, coinHalf);
|
||||
wallet.confirmSend(outbound1);
|
||||
wallet.receive(outbound1, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
// That other guy gives us the coins right back.
|
||||
Transaction inbound2 = new Transaction(params);
|
||||
inbound2.addOutput(new TransactionOutput(params, inbound2, coinHalf, myAddress));
|
||||
inbound2.addInput(outbound1.outputs.get(0));
|
||||
wallet.receive(inbound2, null, BlockChain.NewBlockType.BEST_CHAIN);
|
||||
assertEquals(coin1, wallet.getBalance());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void finneyAttack() throws Exception {
|
||||
// A Finney attack is where a miner includes a transaction spending coins to themselves but does not
|
||||
|
Loading…
x
Reference in New Issue
Block a user