mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2024-11-20 18:22:12 +01:00
Add isStandard risk analysis.
This is currently only to deal with recent spam, especially dust sybil spam. Includes an unit-test by Andreas Schildbach.
This commit is contained in:
parent
52df132a9d
commit
af1fdd4a14
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,11 +17,14 @@
|
||||
|
||||
package com.google.bitcoin.wallet;
|
||||
|
||||
import com.google.bitcoin.core.NetworkParameters;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.bitcoin.core.TransactionOutput;
|
||||
import com.google.bitcoin.core.Wallet;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
@ -34,6 +38,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
protected final List<Transaction> dependencies;
|
||||
protected final Wallet wallet;
|
||||
|
||||
private Transaction nonStandard;
|
||||
protected Transaction nonFinal;
|
||||
protected boolean analyzed;
|
||||
|
||||
@ -48,6 +53,14 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
checkState(!analyzed);
|
||||
analyzed = true;
|
||||
|
||||
Result result = analyzeIsFinal();
|
||||
if (result != Result.OK)
|
||||
return result;
|
||||
|
||||
return analyzeIsStandard();
|
||||
}
|
||||
|
||||
private Result analyzeIsFinal() {
|
||||
// Transactions we create ourselves are, by definition, not at risk of double spending against us.
|
||||
if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF)
|
||||
return Result.OK;
|
||||
@ -71,6 +84,49 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return Result.OK;
|
||||
}
|
||||
|
||||
private Result analyzeIsStandard() {
|
||||
if (!wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
||||
return Result.OK;
|
||||
|
||||
nonStandard = isStandard(tx);
|
||||
if (nonStandard != null)
|
||||
return Result.NON_STANDARD;
|
||||
|
||||
for (Transaction dep : dependencies) {
|
||||
nonStandard = isStandard(dep);
|
||||
if (nonStandard != null)
|
||||
return Result.NON_STANDARD;
|
||||
}
|
||||
|
||||
return Result.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks if a transaction is considered "standard" by the reference client's IsStandardTx and AreInputsStandard
|
||||
* functions.</p>
|
||||
*
|
||||
* <p>Note that this method currently only implements a minimum of checks. More to be added later.</p>
|
||||
*
|
||||
* @return Either null if the transaction is standard, or the first transaction found which is considered nonstandard
|
||||
*/
|
||||
public Transaction isStandard(Transaction tx) {
|
||||
if (tx.getVersion() > 1 || tx.getVersion() < 1)
|
||||
return tx;
|
||||
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
if (output.getMinNonDustValue().compareTo(output.getValue()) > 0)
|
||||
return tx;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the transaction that was found to be non-standard, or null. */
|
||||
@Nullable
|
||||
public Transaction getNonStandard() {
|
||||
return nonStandard;
|
||||
}
|
||||
|
||||
/** Returns the transaction that was found to be non-final, or null. */
|
||||
@Nullable
|
||||
public Transaction getNonFinal() {
|
||||
@ -83,6 +139,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
return "Pending risk analysis for " + tx.getHashAsString();
|
||||
else if (nonFinal != null)
|
||||
return "Risky due to non-finality of " + nonFinal.getHashAsString();
|
||||
else if (nonStandard != null)
|
||||
return "Risky due to non-standard tx " + nonStandard.getHashAsString();
|
||||
else
|
||||
return "Non-risky";
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ import java.util.List;
|
||||
public interface RiskAnalysis {
|
||||
public enum Result {
|
||||
OK,
|
||||
NON_FINAL
|
||||
NON_FINAL,
|
||||
NON_STANDARD
|
||||
}
|
||||
|
||||
public Result analyze();
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,8 +17,10 @@
|
||||
|
||||
package com.google.bitcoin.wallet;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.params.UnitTestParams;
|
||||
import com.google.bitcoin.params.MainNetParams;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -27,7 +30,8 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class DefaultRiskAnalysisTest {
|
||||
private static final NetworkParameters params = UnitTestParams.get();
|
||||
// Uses mainnet because isStandard checks are disabled on testnet.
|
||||
private static final NetworkParameters params = MainNetParams.get();
|
||||
private Wallet wallet;
|
||||
private final int TIMESTAMP = 1384190189;
|
||||
private ECKey key1;
|
||||
@ -119,4 +123,22 @@ public class DefaultRiskAnalysisTest {
|
||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||
assertEquals(tx1, analysis.getNonFinal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonStandardDust() {
|
||||
Transaction standardTx = new Transaction(params);
|
||||
standardTx.addInput(params.getGenesisBlock().getTransactions().get(0).getOutput(0));
|
||||
standardTx.addOutput(Utils.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(BigInteger.ONE, 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(dustTx.getOutput(0).getMinNonDustValue(), key1); // Dust threshold
|
||||
assertEquals(RiskAnalysis.Result.OK, DefaultRiskAnalysis.FACTORY.create(wallet, edgeCaseTx, NO_DEPS).analyze());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user