mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 13:37:24 +00:00
DefaultRiskAnalysis: Consider transactions that opt into replace-by-fee at risk for double spending.
This commit is contained in:
@@ -84,6 +84,12 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF)
|
||||
return Result.OK;
|
||||
|
||||
// We consider transactions that opt into replace-by-fee at risk of double spending.
|
||||
if (tx.isOptInFullRBF()) {
|
||||
nonFinal = tx;
|
||||
return Result.NON_FINAL;
|
||||
}
|
||||
|
||||
if (wallet == null)
|
||||
return null;
|
||||
|
||||
@@ -103,6 +109,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return Result.NON_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
return Result.OK;
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.crypto.*;
|
||||
import org.bitcoinj.params.*;
|
||||
import org.bitcoinj.script.*;
|
||||
import org.bitcoinj.testing.FakeTxBuilder;
|
||||
import org.bitcoinj.wallet.DefaultRiskAnalysis.*;
|
||||
import org.junit.*;
|
||||
|
||||
@@ -54,6 +55,16 @@ public class DefaultRiskAnalysisTest {
|
||||
};
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void analysisCantBeUsedTwice() {
|
||||
Transaction tx = new Transaction(params);
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
assertNull(analysis.getNonFinal());
|
||||
// Verify we can't re-use a used up risk analysis.
|
||||
analysis.analyze();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonFinal() throws Exception {
|
||||
// Verify that just having a lock time in the future is not enough to be considered risky (it's still final).
|
||||
@@ -61,32 +72,20 @@ public class DefaultRiskAnalysisTest {
|
||||
TransactionInput input = tx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0));
|
||||
tx.addOutput(COIN, key1);
|
||||
tx.setLockTime(TIMESTAMP + 86400);
|
||||
|
||||
{
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
assertNull(analysis.getNonFinal());
|
||||
// Verify we can't re-use a used up risk analysis.
|
||||
try {
|
||||
analysis.analyze();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {}
|
||||
}
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
assertNull(analysis.getNonFinal());
|
||||
|
||||
// Set a sequence number on the input to make it genuinely non-final. Verify it's risky.
|
||||
input.setSequenceNumber(1);
|
||||
{
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonFinal());
|
||||
}
|
||||
input.setSequenceNumber(TransactionInput.NO_SEQUENCE - 1);
|
||||
analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonFinal());
|
||||
|
||||
// If the lock time is the current block, it's about to become final and we consider it non-risky.
|
||||
tx.setLockTime(1000);
|
||||
{
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
}
|
||||
analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.OK, analysis.analyze());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,4 +222,13 @@ public class DefaultRiskAnalysisTest {
|
||||
tx.addOutput(Coin.CENT, ScriptBuilder.createOpReturnScript("hi there".getBytes()));
|
||||
assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS).analyze());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optInFullRBF() throws Exception {
|
||||
Transaction tx = FakeTxBuilder.createFakeTx(params, Coin.COIN, key1);
|
||||
tx.getInput(0).setSequenceNumber(TransactionInput.NO_SEQUENCE - 2);
|
||||
DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS);
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx, analysis.getNonFinal());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user