mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 20:11:23 +00:00
Merge remote-tracking branch 'upstream/master' into rebase
Conflicts: core/src/main/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysis.java core/src/test/java/com/dogecoin/dogecoinj/core/ECKeyTest.java core/src/test/java/com/dogecoin/dogecoinj/wallet/DefaultRiskAnalysisTest.java tools/src/main/java/com/dogecoin/dogecoinj/tools/WatchMempool.java
This commit is contained in:
@@ -112,8 +112,8 @@ public class ECKey implements EncryptableItem, Serializable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
// The parameters of the secp256k1 curve that Bitcoin uses.
|
||||||
public static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
|
private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
|
||||||
|
|
||||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
||||||
public static final ECDomainParameters CURVE;
|
public static final ECDomainParameters CURVE;
|
||||||
@@ -495,6 +495,14 @@ public class ECKey implements EncryptableItem, Serializable {
|
|||||||
this.s = s;
|
this.s = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the S component is "low", that means it is below {@link ECKey#HALF_CURVE_ORDER}. See <a
|
||||||
|
* href="https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures">BIP62</a>.
|
||||||
|
*/
|
||||||
|
public boolean isCanonical() {
|
||||||
|
return s.compareTo(HALF_CURVE_ORDER) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will automatically adjust the S component to be less than or equal to half the curve order, if necessary.
|
* Will automatically adjust the S component to be less than or equal to half the curve order, if necessary.
|
||||||
* This is required because for every signature (r,s) the signature (r, -s (mod N)) is a valid signature of
|
* This is required because for every signature (r,s) the signature (r, -s (mod N)) is a valid signature of
|
||||||
@@ -503,7 +511,7 @@ public class ECKey implements EncryptableItem, Serializable {
|
|||||||
* considered legal and the other will be banned.
|
* considered legal and the other will be banned.
|
||||||
*/
|
*/
|
||||||
public ECDSASignature toCanonicalised() {
|
public ECDSASignature toCanonicalised() {
|
||||||
if (s.compareTo(HALF_CURVE_ORDER) > 0) {
|
if (!isCanonical()) {
|
||||||
// The order of the curve is the number of valid points that exist on that curve. If S is in the upper
|
// The order of the curve is the number of valid points that exist on that curve. If S is in the upper
|
||||||
// half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that
|
// half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that
|
||||||
// N = 10
|
// N = 10
|
||||||
@@ -1204,6 +1212,8 @@ public class ECKey implements EncryptableItem, Serializable {
|
|||||||
builder.append(address.toString());
|
builder.append(address.toString());
|
||||||
builder.append(" hash160:");
|
builder.append(" hash160:");
|
||||||
builder.append(Utils.HEX.encode(getPubKeyHash()));
|
builder.append(Utils.HEX.encode(getPubKeyHash()));
|
||||||
|
if (creationTimeSeconds > 0)
|
||||||
|
builder.append(" creationTimeSeconds:").append(creationTimeSeconds);
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
if (includePrivateKeys) {
|
if (includePrivateKeys) {
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
|
@@ -103,7 +103,7 @@ public class TransactionBroadcast {
|
|||||||
List<Peer> peers = peerGroup.getConnectedPeers(); // snapshots
|
List<Peer> peers = peerGroup.getConnectedPeers(); // snapshots
|
||||||
// We intern the tx here so we are using a canonical version of the object (as it's unfortunately mutable).
|
// We intern the tx here so we are using a canonical version of the object (as it's unfortunately mutable).
|
||||||
// TODO: Once confidence state is moved out of Transaction we can kill off this step.
|
// TODO: Once confidence state is moved out of Transaction we can kill off this step.
|
||||||
pinnedTx = context != null ? context.getConfidenceTable().intern(tx) : pinnedTx;
|
pinnedTx = context != null ? context.getConfidenceTable().intern(tx) : tx;
|
||||||
// Prepare to send the transaction by adding a listener that'll be called when confidence changes.
|
// Prepare to send the transaction by adding a listener that'll be called when confidence changes.
|
||||||
// Only bother with this if we might actually hear back:
|
// Only bother with this if we might actually hear back:
|
||||||
if (minConnections > 1)
|
if (minConnections > 1)
|
||||||
|
@@ -60,7 +60,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
|||||||
transient private WeakReference<Script> scriptSig;
|
transient private WeakReference<Script> scriptSig;
|
||||||
/** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */
|
/** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Coin value;
|
private Coin value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an input that connects to nothing - used only in creation of coinbase transactions.
|
* Creates an input that connects to nothing - used only in creation of coinbase transactions.
|
||||||
@@ -375,6 +375,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
|||||||
public void connect(TransactionOutput out) {
|
public void connect(TransactionOutput out) {
|
||||||
outpoint.fromTx = out.getParentTransaction();
|
outpoint.fromTx = out.getParentTransaction();
|
||||||
out.markAsSpent(this);
|
out.markAsSpent(this);
|
||||||
|
value = out.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2477,6 +2477,25 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the wallet for a blockchain replay. Removes all transactions (as they would get in the way of the
|
||||||
|
* replay) and makes the wallet think it has never seen a block. {@link WalletEventListener#onWalletChanged()} will
|
||||||
|
* be fired.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
clearTransactions();
|
||||||
|
lastBlockSeenHash = null;
|
||||||
|
lastBlockSeenHeight = -1; // Magic value for 'never'.
|
||||||
|
lastBlockSeenTimeSecs = 0;
|
||||||
|
saveLater();
|
||||||
|
maybeQueueOnWalletChanged();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes transactions which appeared above the given block height from the wallet, but does not touch the keys.
|
* Deletes transactions which appeared above the given block height from the wallet, but does not touch the keys.
|
||||||
* This is useful if you have some keys and wish to replay the block chain into the wallet in order to pick them up.
|
* This is useful if you have some keys and wish to replay the block chain into the wallet in order to pick them up.
|
||||||
@@ -2486,11 +2505,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
if (fromHeight == 0) {
|
if (fromHeight == 0) {
|
||||||
unspent.clear();
|
clearTransactions();
|
||||||
spent.clear();
|
|
||||||
pending.clear();
|
|
||||||
dead.clear();
|
|
||||||
transactions.clear();
|
|
||||||
saveLater();
|
saveLater();
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
@@ -2500,6 +2515,14 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearTransactions() {
|
||||||
|
unspent.clear();
|
||||||
|
spent.clear();
|
||||||
|
pending.clear();
|
||||||
|
dead.clear();
|
||||||
|
transactions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the outputs that match addresses or scripts added via {@link #addWatchedAddress(Address)} or
|
* Returns all the outputs that match addresses or scripts added via {@link #addWatchedAddress(Address)} or
|
||||||
* {@link #addWatchedScripts(java.util.List)}.
|
* {@link #addWatchedScripts(java.util.List)}.
|
||||||
|
@@ -107,7 +107,8 @@ public interface WalletEventListener extends KeyChainEventListener {
|
|||||||
* <li>A pending transaction changes confidence due to some non-new-block related event, such as being
|
* <li>A pending transaction changes confidence due to some non-new-block related event, such as being
|
||||||
* announced by more peers or by a double-spend conflict being observed.</li>
|
* announced by more peers or by a double-spend conflict being observed.</li>
|
||||||
* <li>A re-organize occurs. Call occurs only if the re-org modified any of our transactions.</li>
|
* <li>A re-organize occurs. Call occurs only if the re-org modified any of our transactions.</li>
|
||||||
* <li>A new spend is committed to the wallet</li>
|
* <li>A new spend is committed to the wallet.</li>
|
||||||
|
* <li>The wallet is reset and all transactions removed.<li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
* <p>When this is called you can refresh the UI contents from the wallet contents. It's more efficient to use
|
* <p>When this is called you can refresh the UI contents from the wallet contents. It's more efficient to use
|
||||||
|
@@ -153,7 +153,7 @@ public class BIP38PrivateKey extends VersionedChecksummedBytes {
|
|||||||
BigInteger seedFactor = new BigInteger(1, Sha256Hash.createDouble(seed).getBytes());
|
BigInteger seedFactor = new BigInteger(1, Sha256Hash.createDouble(seed).getBytes());
|
||||||
checkState(passFactor.signum() >= 0);
|
checkState(passFactor.signum() >= 0);
|
||||||
checkState(seedFactor.signum() >= 0);
|
checkState(seedFactor.signum() >= 0);
|
||||||
BigInteger priv = passFactor.multiply(seedFactor).mod(ECKey.CURVE_PARAMS.getN());
|
BigInteger priv = passFactor.multiply(seedFactor).mod(ECKey.CURVE.getN());
|
||||||
|
|
||||||
return ECKey.fromPrivate(priv, compressed);
|
return ECKey.fromPrivate(priv, compressed);
|
||||||
} catch (GeneralSecurityException x) {
|
} catch (GeneralSecurityException x) {
|
||||||
|
@@ -18,12 +18,15 @@
|
|||||||
package com.dogecoin.dogecoinj.wallet;
|
package com.dogecoin.dogecoinj.wallet;
|
||||||
|
|
||||||
import com.dogecoin.dogecoinj.core.Coin;
|
import com.dogecoin.dogecoinj.core.Coin;
|
||||||
|
import com.dogecoin.dogecoinj.core.ECKey;
|
||||||
|
import com.dogecoin.dogecoinj.core.ECKey.ECDSASignature;
|
||||||
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
||||||
import com.dogecoin.dogecoinj.core.Transaction;
|
import com.dogecoin.dogecoinj.core.Transaction;
|
||||||
import com.dogecoin.dogecoinj.core.TransactionConfidence;
|
import com.dogecoin.dogecoinj.core.TransactionConfidence;
|
||||||
import com.dogecoin.dogecoinj.core.TransactionInput;
|
import com.dogecoin.dogecoinj.core.TransactionInput;
|
||||||
import com.dogecoin.dogecoinj.core.TransactionOutput;
|
import com.dogecoin.dogecoinj.core.TransactionOutput;
|
||||||
import com.dogecoin.dogecoinj.core.Wallet;
|
import com.dogecoin.dogecoinj.core.Wallet;
|
||||||
|
import com.dogecoin.dogecoinj.crypto.TransactionSignature;
|
||||||
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -51,7 +54,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
|
|
||||||
protected final Transaction tx;
|
protected final Transaction tx;
|
||||||
protected final List<Transaction> dependencies;
|
protected final List<Transaction> dependencies;
|
||||||
protected final Wallet wallet;
|
protected final @Nullable Wallet wallet;
|
||||||
|
|
||||||
private Transaction nonStandard;
|
private Transaction nonStandard;
|
||||||
protected Transaction nonFinal;
|
protected Transaction nonFinal;
|
||||||
@@ -69,17 +72,20 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
analyzed = true;
|
analyzed = true;
|
||||||
|
|
||||||
Result result = analyzeIsFinal();
|
Result result = analyzeIsFinal();
|
||||||
if (result != Result.OK)
|
if (result != null && result != Result.OK)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return analyzeIsStandard();
|
return analyzeIsStandard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result analyzeIsFinal() {
|
private @Nullable 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;
|
||||||
|
|
||||||
|
if (wallet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
final int height = wallet.getLastBlockSeenHeight();
|
final int height = wallet.getLastBlockSeenHeight();
|
||||||
final long time = wallet.getLastBlockSeenTimeSecs();
|
final long time = wallet.getLastBlockSeenTimeSecs();
|
||||||
// If the transaction has a lock time specified in blocks, we consider that if the tx would become final in the
|
// If the transaction has a lock time specified in blocks, we consider that if the tx would become final in the
|
||||||
@@ -108,7 +114,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
VERSION,
|
VERSION,
|
||||||
DUST,
|
DUST,
|
||||||
SHORTEST_POSSIBLE_PUSHDATA,
|
SHORTEST_POSSIBLE_PUSHDATA,
|
||||||
NONEMPTY_STACK // Not yet implemented (for post 0.12)
|
NONEMPTY_STACK, // Not yet implemented (for post 0.12)
|
||||||
|
SIGNATURE_CANONICAL_ENCODING
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,6 +172,19 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
for (ScriptChunk chunk : input.getScriptSig().getChunks()) {
|
for (ScriptChunk chunk : input.getScriptSig().getChunks()) {
|
||||||
if (chunk.data != null && !chunk.isShortestPossiblePushData())
|
if (chunk.data != null && !chunk.isShortestPossiblePushData())
|
||||||
return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA;
|
return RuleViolation.SHORTEST_POSSIBLE_PUSHDATA;
|
||||||
|
if (chunk.isPushData()) {
|
||||||
|
ECDSASignature signature;
|
||||||
|
try {
|
||||||
|
signature = ECKey.ECDSASignature.decodeFromDER(chunk.data);
|
||||||
|
} catch (RuntimeException x) {
|
||||||
|
// Doesn't look like a signature.
|
||||||
|
signature = null;
|
||||||
|
}
|
||||||
|
if (signature != null) {
|
||||||
|
if (!TransactionSignature.isEncodingCanonical(chunk.data))
|
||||||
|
return RuleViolation.SIGNATURE_CANONICAL_ENCODING;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return RuleViolation.NONE;
|
return RuleViolation.NONE;
|
||||||
}
|
}
|
||||||
@@ -172,7 +192,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
|||||||
private Result analyzeIsStandard() {
|
private Result analyzeIsStandard() {
|
||||||
// The IsStandard rules don't apply on testnet, because they're just a safety mechanism and we don't want to
|
// The IsStandard rules don't apply on testnet, because they're just a safety mechanism and we don't want to
|
||||||
// crush innovation with valueless test coins.
|
// crush innovation with valueless test coins.
|
||||||
if (!wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
if (wallet != null && !wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
||||||
return Result.OK;
|
return Result.OK;
|
||||||
|
|
||||||
RuleViolation ruleViolation = isStandard(tx);
|
RuleViolation ruleViolation = isStandard(tx);
|
||||||
|
@@ -244,6 +244,8 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
|||||||
builder.append(script.getToAddress(params));
|
builder.append(script.getToAddress(params));
|
||||||
builder.append(" hash160:");
|
builder.append(" hash160:");
|
||||||
builder.append(Utils.HEX.encode(script.getPubKeyHash()));
|
builder.append(Utils.HEX.encode(script.getPubKeyHash()));
|
||||||
|
if (script.getCreationTimeSeconds() > 0)
|
||||||
|
builder.append(" creationTimeSeconds:").append(script.getCreationTimeSeconds());
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.dogecoin.dogecoinj.core;
|
package com.dogecoin.dogecoinj.core;
|
||||||
|
|
||||||
|
import com.dogecoin.dogecoinj.core.ECKey.ECDSASignature;
|
||||||
import com.dogecoin.dogecoinj.crypto.EncryptedData;
|
import com.dogecoin.dogecoinj.crypto.EncryptedData;
|
||||||
import com.dogecoin.dogecoinj.crypto.KeyCrypter;
|
import com.dogecoin.dogecoinj.crypto.KeyCrypter;
|
||||||
import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt;
|
import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt;
|
||||||
@@ -96,11 +97,15 @@ public class ECKeyTest {
|
|||||||
}
|
}
|
||||||
List<ECKey.ECDSASignature> sigs = Futures.allAsList(sigFutures).get();
|
List<ECKey.ECDSASignature> sigs = Futures.allAsList(sigFutures).get();
|
||||||
for (ECKey.ECDSASignature signature : sigs) {
|
for (ECKey.ECDSASignature signature : sigs) {
|
||||||
assertTrue(signature.s.compareTo(ECKey.HALF_CURVE_ORDER) <= 0);
|
assertTrue(signature.isCanonical());
|
||||||
}
|
}
|
||||||
final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(sigs.get(0).r, sigs.get(0).s);
|
final ECDSASignature first = sigs.get(0);
|
||||||
assertEquals(sigs.get(0), duplicate);
|
final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(first.r, first.s);
|
||||||
assertEquals(sigs.get(0).hashCode(), duplicate.hashCode());
|
assertEquals(first, duplicate);
|
||||||
|
assertEquals(first.hashCode(), duplicate.hashCode());
|
||||||
|
|
||||||
|
final ECKey.ECDSASignature highS = new ECKey.ECDSASignature(first.r, ECKey.CURVE.getN().subtract(first.s));
|
||||||
|
assertFalse(highS.isCanonical());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -17,12 +17,15 @@
|
|||||||
|
|
||||||
package com.dogecoin.dogecoinj.wallet;
|
package com.dogecoin.dogecoinj.wallet;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import java.util.Arrays;
|
||||||
import com.dogecoin.dogecoinj.core.*;
|
import com.dogecoin.dogecoinj.core.*;
|
||||||
|
import com.dogecoin.dogecoinj.crypto.TransactionSignature;
|
||||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||||
|
import com.dogecoin.dogecoinj.script.Script;
|
||||||
import com.dogecoin.dogecoinj.script.ScriptBuilder;
|
import com.dogecoin.dogecoinj.script.ScriptBuilder;
|
||||||
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.dogecoin.dogecoinj.wallet.DefaultRiskAnalysis.RuleViolation;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -160,6 +163,22 @@ public class DefaultRiskAnalysisTest {
|
|||||||
assertEquals(DefaultRiskAnalysis.RuleViolation.SHORTEST_POSSIBLE_PUSHDATA, DefaultRiskAnalysis.isStandard(tx));
|
assertEquals(DefaultRiskAnalysis.RuleViolation.SHORTEST_POSSIBLE_PUSHDATA, DefaultRiskAnalysis.isStandard(tx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canonicalSignature() {
|
||||||
|
TransactionSignature sig = TransactionSignature.dummy();
|
||||||
|
Script scriptOk = ScriptBuilder.createInputScript(sig);
|
||||||
|
assertEquals(RuleViolation.NONE,
|
||||||
|
DefaultRiskAnalysis.isInputStandard(new TransactionInput(params, null, scriptOk.getProgram())));
|
||||||
|
|
||||||
|
byte[] sigBytes = sig.encodeToBitcoin();
|
||||||
|
// Appending a zero byte makes the signature uncanonical without violating DER encoding.
|
||||||
|
Script scriptUncanonicalEncoding = new ScriptBuilder().data(Arrays.copyOf(sigBytes, sigBytes.length + 1))
|
||||||
|
.build();
|
||||||
|
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING,
|
||||||
|
DefaultRiskAnalysis.isInputStandard(new TransactionInput(params, null, scriptUncanonicalEncoding
|
||||||
|
.getProgram())));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void standardOutputs() throws Exception {
|
public void standardOutputs() throws Exception {
|
||||||
Transaction tx = new Transaction(params);
|
Transaction tx = new Transaction(params);
|
||||||
|
@@ -17,35 +17,73 @@
|
|||||||
|
|
||||||
package com.dogecoin.dogecoinj.tools;
|
package com.dogecoin.dogecoinj.tools;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.dogecoin.dogecoinj.core.AbstractPeerEventListener;
|
||||||
|
import com.dogecoin.dogecoinj.core.NetworkParameters;
|
||||||
import com.dogecoin.dogecoinj.core.*;
|
import com.dogecoin.dogecoinj.core.*;
|
||||||
|
import com.dogecoin.dogecoinj.core.PeerGroup;
|
||||||
|
import com.dogecoin.dogecoinj.core.Transaction;
|
||||||
import com.dogecoin.dogecoinj.net.discovery.DnsDiscovery;
|
import com.dogecoin.dogecoinj.net.discovery.DnsDiscovery;
|
||||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||||
import com.dogecoin.dogecoinj.utils.BriefLogFormatter;
|
import com.dogecoin.dogecoinj.utils.BriefLogFormatter;
|
||||||
|
import com.dogecoin.dogecoinj.wallet.DefaultRiskAnalysis;
|
||||||
|
import com.dogecoin.dogecoinj.wallet.RiskAnalysis.Result;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
public class WatchMempool {
|
public class WatchMempool {
|
||||||
private static Logger log = LoggerFactory.getLogger(WatchMempool.class);
|
private static Logger log = LoggerFactory.getLogger(WatchMempool.class);
|
||||||
|
private static final NetworkParameters PARAMS = MainNetParams.get();
|
||||||
|
private static final ImmutableList<Transaction> NO_DEPS = ImmutableList.of();
|
||||||
|
private static final Map<String, Integer> counters = new HashMap<String, Integer>();
|
||||||
|
private static final String TOTAL_KEY = "TOTAL";
|
||||||
|
private static final long START_MS = System.currentTimeMillis();
|
||||||
|
private static final long STATISTICS_FREQUENCY_MS = 1000 * 5;
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException {
|
public static void main(String[] args) throws InterruptedException {
|
||||||
BriefLogFormatter.init();
|
BriefLogFormatter.init();
|
||||||
NetworkParameters params = MainNetParams.get();
|
PeerGroup peerGroup = new PeerGroup(PARAMS);
|
||||||
PeerGroup peerGroup = new PeerGroup(params);
|
peerGroup.setMaxConnections(32);
|
||||||
peerGroup.addPeerDiscovery(new DnsDiscovery(params));
|
peerGroup.addPeerDiscovery(new DnsDiscovery(PARAMS));
|
||||||
peerGroup.addEventListener(new AbstractPeerEventListener() {
|
peerGroup.addEventListener(new AbstractPeerEventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTransaction(Peer peer, Transaction tx) {
|
public void onTransaction(Peer peer, Transaction tx) {
|
||||||
try {
|
Result result = DefaultRiskAnalysis.FACTORY.create(null, tx, NO_DEPS).analyze();
|
||||||
log.info("tx {}", tx.getHash());
|
incrementCounter(TOTAL_KEY);
|
||||||
if (tx.getOutputs().size() != 1) return;
|
log.info("tx {} result {}", tx.getHash(), result);
|
||||||
if (!tx.getOutput(0).getScriptPubKey().isSentToRawPubKey()) return;
|
incrementCounter(result.name());
|
||||||
log.info("Saw raw pay to pubkey {}", tx);
|
if (result == Result.NON_STANDARD)
|
||||||
} catch (ScriptException e) {
|
incrementCounter(Result.NON_STANDARD + "-" + DefaultRiskAnalysis.isStandard(tx));
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
peerGroup.start();
|
peerGroup.start();
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
|
while (true) {
|
||||||
|
Thread.sleep(STATISTICS_FREQUENCY_MS);
|
||||||
|
printCounters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static void incrementCounter(String name) {
|
||||||
|
Integer count = counters.get(name);
|
||||||
|
if (count == null)
|
||||||
|
count = 0;
|
||||||
|
count++;
|
||||||
|
counters.put(name, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static void printCounters() {
|
||||||
|
System.out.printf("Runtime: %d minutes\n", (System.currentTimeMillis() - START_MS) / 1000 / 60);
|
||||||
|
Integer total = counters.get(TOTAL_KEY);
|
||||||
|
if (total == null)
|
||||||
|
return;
|
||||||
|
for (Map.Entry<String, Integer> counter : counters.entrySet()) {
|
||||||
|
System.out.printf(" %-40s%6d (%d%% of total)\n", counter.getKey(), counter.getValue(),
|
||||||
|
(int) counter.getValue() * 100 / total);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,12 @@
|
|||||||
</filters>
|
</filters>
|
||||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||||
<shadedClassifierName>bundled</shadedClassifierName>
|
<shadedClassifierName>bundled</shadedClassifierName>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>${artifactId}.Main</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<outputFile>target/${artifactId}-shaded.jar</outputFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
Reference in New Issue
Block a user