@Override public void onTransaction(Peer peer, Transaction tx) { Result result = DefaultRiskAnalysis.FACTORY.create(null, tx, NO_DEPS).analyze(); incrementCounter(TOTAL_KEY); log.info("tx {} result {}", tx.getHash(), result); incrementCounter(result.name()); if (result == Result.NON_STANDARD) incrementCounter(Result.NON_STANDARD + "-" + DefaultRiskAnalysis.isStandard(tx)); } });
@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 nonStandardDust() { Transaction standardTx = new Transaction(PARAMS); standardTx.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)); standardTx.addOutput(COIN, key1); assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, standardTx, NO_DEPS).analyze()); Transaction dustTx = new Transaction(PARAMS); dustTx.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)); dustTx.addOutput(Coin.SATOSHI, key1); // 1 Satoshi assertEquals(RiskAnalysis.Result.NON_STANDARD, DefaultRiskAnalysis.FACTORY.create(wallet, dustTx, NO_DEPS).analyze()); Transaction edgeCaseTx = new Transaction(PARAMS); edgeCaseTx.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)); edgeCaseTx.addOutput(DefaultRiskAnalysis.MIN_ANALYSIS_NONDUST_OUTPUT, key1); // Dust threshold assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, edgeCaseTx, NO_DEPS).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). Transaction tx = new Transaction(PARAMS); 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()); // Set a sequence number on the input to make it genuinely non-final. Verify it's risky. 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); analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); assertEquals(RiskAnalysis.Result.OK, analysis.analyze()); }
@Test public void selfCreatedAreNotRisky() { Transaction tx = new Transaction(PARAMS); tx.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)).setSequenceNumber(1); tx.addOutput(COIN, key1); tx.setLockTime(TIMESTAMP + 86400); { // Is risky ... DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze()); } tx.getConfidence().setSource(TransactionConfidence.Source.SELF); { // Is no longer risky. DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS); assertEquals(RiskAnalysis.Result.OK, analysis.analyze()); } }
@Test public void optInFullRBF() throws Exception { Transaction tx = FakeTxBuilder.createFakeTx(PARAMS); 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()); } }
@Test public void nonFinalDependency() { // Final tx has a dependency that is non-final. Transaction tx1 = new Transaction(PARAMS); tx1.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)).setSequenceNumber(1); TransactionOutput output = tx1.addOutput(COIN, key1); tx1.setLockTime(TIMESTAMP + 86400); Transaction tx2 = new Transaction(PARAMS); tx2.addInput(output); tx2.addOutput(COIN, new ECKey()); DefaultRiskAnalysis analysis = DefaultRiskAnalysis.FACTORY.create(wallet, tx2, ImmutableList.of(tx1)); assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze()); assertEquals(tx1, analysis.getNonFinal()); }
@Test public void standardOutputs() throws Exception { Transaction tx = new Transaction(PARAMS); tx.addInput(PARAMS.getGenesisBlock().getTransactions().get(0).getOutput(0)); // A pay to address output tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1.toAddress(PARAMS))); // A pay to pubkey output tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1)); tx.addOutput(Coin.CENT, ScriptBuilder.createOutputScript(key1)); // 1-of-2 multisig output. ImmutableList<ECKey> keys = ImmutableList.of(key1, new ECKey()); tx.addOutput(Coin.CENT, ScriptBuilder.createMultiSigOutputScript(1, keys)); // 2-of-2 multisig output. tx.addOutput(Coin.CENT, ScriptBuilder.createMultiSigOutputScript(2, keys)); // P2SH tx.addOutput(Coin.CENT, ScriptBuilder.createP2SHOutputScript(1, keys)); // OP_RETURN tx.addOutput(Coin.CENT, ScriptBuilder.createOpReturnScript("hi there".getBytes())); assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, tx, NO_DEPS).analyze()); }