mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 23:03:04 +00: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 2013 Google Inc.
|
||||||
|
* Copyright 2014 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,11 +17,14 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.wallet;
|
package com.google.bitcoin.wallet;
|
||||||
|
|
||||||
|
import com.google.bitcoin.core.NetworkParameters;
|
||||||
import com.google.bitcoin.core.Transaction;
|
import com.google.bitcoin.core.Transaction;
|
||||||
import com.google.bitcoin.core.TransactionConfidence;
|
import com.google.bitcoin.core.TransactionConfidence;
|
||||||
|
import com.google.bitcoin.core.TransactionOutput;
|
||||||
import com.google.bitcoin.core.Wallet;
|
import com.google.bitcoin.core.Wallet;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
@ -34,6 +38,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
protected final List<Transaction> dependencies;
|
protected final List<Transaction> dependencies;
|
||||||
protected final Wallet wallet;
|
protected final Wallet wallet;
|
||||||
|
|
||||||
|
private Transaction nonStandard;
|
||||||
protected Transaction nonFinal;
|
protected Transaction nonFinal;
|
||||||
protected boolean analyzed;
|
protected boolean analyzed;
|
||||||
|
|
||||||
@ -48,6 +53,14 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
checkState(!analyzed);
|
checkState(!analyzed);
|
||||||
analyzed = true;
|
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.
|
// Transactions we create ourselves are, by definition, not at risk of double spending against us.
|
||||||
if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF)
|
if (tx.getConfidence().getSource() == TransactionConfidence.Source.SELF)
|
||||||
return Result.OK;
|
return Result.OK;
|
||||||
@ -71,6 +84,49 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
return Result.OK;
|
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. */
|
/** Returns the transaction that was found to be non-final, or null. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public Transaction getNonFinal() {
|
public Transaction getNonFinal() {
|
||||||
@ -83,6 +139,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
return "Pending risk analysis for " + tx.getHashAsString();
|
return "Pending risk analysis for " + tx.getHashAsString();
|
||||||
else if (nonFinal != null)
|
else if (nonFinal != null)
|
||||||
return "Risky due to non-finality of " + nonFinal.getHashAsString();
|
return "Risky due to non-finality of " + nonFinal.getHashAsString();
|
||||||
|
else if (nonStandard != null)
|
||||||
|
return "Risky due to non-standard tx " + nonStandard.getHashAsString();
|
||||||
else
|
else
|
||||||
return "Non-risky";
|
return "Non-risky";
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ import java.util.List;
|
|||||||
public interface RiskAnalysis {
|
public interface RiskAnalysis {
|
||||||
public enum Result {
|
public enum Result {
|
||||||
OK,
|
OK,
|
||||||
NON_FINAL
|
NON_FINAL,
|
||||||
|
NON_STANDARD
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result analyze();
|
public Result analyze();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Google Inc.
|
* Copyright 2013 Google Inc.
|
||||||
|
* Copyright 2014 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,8 +17,10 @@
|
|||||||
|
|
||||||
package com.google.bitcoin.wallet;
|
package com.google.bitcoin.wallet;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import com.google.bitcoin.core.*;
|
import com.google.bitcoin.core.*;
|
||||||
import com.google.bitcoin.params.UnitTestParams;
|
import com.google.bitcoin.params.MainNetParams;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -27,7 +30,8 @@ import static org.junit.Assert.assertNull;
|
|||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class DefaultRiskAnalysisTest {
|
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 Wallet wallet;
|
||||||
private final int TIMESTAMP = 1384190189;
|
private final int TIMESTAMP = 1384190189;
|
||||||
private ECKey key1;
|
private ECKey key1;
|
||||||
@ -119,4 +123,22 @@ public class DefaultRiskAnalysisTest {
|
|||||||
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
assertEquals(RiskAnalysis.Result.NON_FINAL, analysis.analyze());
|
||||||
assertEquals(tx1, analysis.getNonFinal());
|
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…
x
Reference in New Issue
Block a user