mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 20:11:23 +00:00
Wallet: fix a bug that could cause a temporarily corrupted balance, when two pending transactions arrive backwards
This commit is contained in:
@@ -1975,23 +1975,28 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
// ever occur because we expect transactions to arrive in temporal order, but this assumption can be violated
|
// ever occur because we expect transactions to arrive in temporal order, but this assumption can be violated
|
||||||
// when we receive a pending transaction from the mempool that is relevant to us, which spends coins that we
|
// when we receive a pending transaction from the mempool that is relevant to us, which spends coins that we
|
||||||
// didn't see arrive on the best chain yet. For instance, because of a chain replay or because of our keys were
|
// didn't see arrive on the best chain yet. For instance, because of a chain replay or because of our keys were
|
||||||
// used by another wallet somewhere else.
|
// used by another wallet somewhere else. Also, unconfirmed transactions can arrive from the mempool in more or
|
||||||
if (fromChain) {
|
// less random order.
|
||||||
for (Transaction pendingTx : pending.values()) {
|
for (Transaction pendingTx : pending.values()) {
|
||||||
for (TransactionInput input : pendingTx.getInputs()) {
|
for (TransactionInput input : pendingTx.getInputs()) {
|
||||||
TransactionInput.ConnectionResult result = input.connect(tx, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
|
TransactionInput.ConnectionResult result = input.connect(tx, TransactionInput.ConnectMode.ABORT_ON_CONFLICT);
|
||||||
|
if (fromChain) {
|
||||||
// This TX is supposed to have just appeared on the best chain, so its outputs should not be marked
|
// This TX is supposed to have just appeared on the best chain, so its outputs should not be marked
|
||||||
// as spent yet. If they are, it means something is happening out of order.
|
// as spent yet. If they are, it means something is happening out of order.
|
||||||
checkState(result != TransactionInput.ConnectionResult.ALREADY_SPENT);
|
checkState(result != TransactionInput.ConnectionResult.ALREADY_SPENT);
|
||||||
if (result == TransactionInput.ConnectionResult.SUCCESS) {
|
|
||||||
log.info("Connected pending tx input {}:{}",
|
|
||||||
pendingTx.getHashAsString(), pendingTx.getInputs().indexOf(input));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the transactions outputs are now all spent, it will be moved into the spent pool by the
|
if (result == TransactionInput.ConnectionResult.SUCCESS) {
|
||||||
// processTxFromBestChain method.
|
log.info("Connected pending tx input {}:{}",
|
||||||
|
pendingTx.getHashAsString(), pendingTx.getInputs().indexOf(input));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!fromChain) {
|
||||||
|
maybeMovePool(tx, "pendingtx");
|
||||||
|
} else {
|
||||||
|
// If the transactions outputs are now all spent, it will be moved into the spent pool by the
|
||||||
|
// processTxFromBestChain method.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the wallet when a double spend occurs. overridingTx can be null for the case of coinbases
|
// Updates the wallet when a double spend occurs. overridingTx can be null for the case of coinbases
|
||||||
|
@@ -71,6 +71,9 @@ public class WalletTest extends TestWithWallet {
|
|||||||
|
|
||||||
private SecureRandom secureRandom = new SecureRandom();
|
private SecureRandom secureRandom = new SecureRandom();
|
||||||
|
|
||||||
|
private ECKey someOtherKey = new ECKey();
|
||||||
|
private Address someOtherAddress = someOtherKey.toAddress(params);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
@@ -1467,7 +1470,7 @@ public class WalletTest extends TestWithWallet {
|
|||||||
Transaction tx1 = createFakeTx(params, value, myAddress);
|
Transaction tx1 = createFakeTx(params, value, myAddress);
|
||||||
Transaction tx2 = new Transaction(params);
|
Transaction tx2 = new Transaction(params);
|
||||||
tx2.addInput(tx1.getOutput(0));
|
tx2.addInput(tx1.getOutput(0));
|
||||||
tx2.addOutput(valueOf(0, 9), new ECKey());
|
tx2.addOutput(valueOf(0, 9), someOtherAddress);
|
||||||
// Add a change address to ensure this tx is relevant.
|
// Add a change address to ensure this tx is relevant.
|
||||||
tx2.addOutput(CENT, wallet.getChangeAddress());
|
tx2.addOutput(CENT, wallet.getChangeAddress());
|
||||||
wallet.receivePending(tx2, null);
|
wallet.receivePending(tx2, null);
|
||||||
@@ -1480,6 +1483,26 @@ public class WalletTest extends TestWithWallet {
|
|||||||
assertEquals(0, wallet.getPoolSize(Pool.UNSPENT));
|
assertEquals(0, wallet.getPoolSize(Pool.UNSPENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void outOfOrderPendingTxns() throws Exception {
|
||||||
|
// Check that if there are two pending transactions which we receive out of order, they are marked as spent
|
||||||
|
// correctly. For instance, we are watching a wallet, someone pays us (A) and we then pay someone else (B)
|
||||||
|
// with a change address but the network delivers the transactions to us in order B then A.
|
||||||
|
Coin value = COIN;
|
||||||
|
Transaction a = createFakeTx(params, value, myAddress);
|
||||||
|
Transaction b = new Transaction(params);
|
||||||
|
b.addInput(a.getOutput(0));
|
||||||
|
b.addOutput(CENT, someOtherAddress);
|
||||||
|
Coin v = COIN.subtract(CENT);
|
||||||
|
b.addOutput(v, wallet.getChangeAddress());
|
||||||
|
a = roundTripTransaction(params, a);
|
||||||
|
b = roundTripTransaction(params, b);
|
||||||
|
wallet.receivePending(b, null);
|
||||||
|
assertEquals(v, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||||
|
wallet.receivePending(a, null);
|
||||||
|
assertEquals(v, wallet.getBalance(Wallet.BalanceType.ESTIMATED));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encryptionDecryptionAESBasic() throws Exception {
|
public void encryptionDecryptionAESBasic() throws Exception {
|
||||||
Wallet encryptedWallet = new Wallet(params);
|
Wallet encryptedWallet = new Wallet(params);
|
||||||
|
Reference in New Issue
Block a user