mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-11 17:55:53 +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:
commit
e6fc3deb50
@ -112,8 +112,8 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
}
|
||||
};
|
||||
|
||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
||||
public static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
|
||||
// The parameters of the secp256k1 curve that Bitcoin uses.
|
||||
private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
|
||||
|
||||
/** The parameters of the secp256k1 curve that Bitcoin uses. */
|
||||
public static final ECDomainParameters CURVE;
|
||||
@ -495,6 +495,14 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
// half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that
|
||||
// N = 10
|
||||
@ -1204,6 +1212,8 @@ public class ECKey implements EncryptableItem, Serializable {
|
||||
builder.append(address.toString());
|
||||
builder.append(" hash160:");
|
||||
builder.append(Utils.HEX.encode(getPubKeyHash()));
|
||||
if (creationTimeSeconds > 0)
|
||||
builder.append(" creationTimeSeconds:").append(creationTimeSeconds);
|
||||
builder.append("\n");
|
||||
if (includePrivateKeys) {
|
||||
builder.append(" ");
|
||||
|
@ -103,7 +103,7 @@ public class TransactionBroadcast {
|
||||
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).
|
||||
// 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.
|
||||
// Only bother with this if we might actually hear back:
|
||||
if (minConnections > 1)
|
||||
|
@ -60,7 +60,7 @@ public class TransactionInput extends ChildMessage implements Serializable {
|
||||
transient private WeakReference<Script> scriptSig;
|
||||
/** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */
|
||||
@Nullable
|
||||
private final Coin value;
|
||||
private Coin value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
outpoint.fromTx = out.getParentTransaction();
|
||||
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.
|
||||
* 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();
|
||||
try {
|
||||
if (fromHeight == 0) {
|
||||
unspent.clear();
|
||||
spent.clear();
|
||||
pending.clear();
|
||||
dead.clear();
|
||||
transactions.clear();
|
||||
clearTransactions();
|
||||
saveLater();
|
||||
} else {
|
||||
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
|
||||
* {@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
|
||||
* 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 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>
|
||||
*
|
||||
* <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());
|
||||
checkState(passFactor.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);
|
||||
} catch (GeneralSecurityException x) {
|
||||
|
@ -18,12 +18,15 @@
|
||||
package com.dogecoin.dogecoinj.wallet;
|
||||
|
||||
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.Transaction;
|
||||
import com.dogecoin.dogecoinj.core.TransactionConfidence;
|
||||
import com.dogecoin.dogecoinj.core.TransactionInput;
|
||||
import com.dogecoin.dogecoinj.core.TransactionOutput;
|
||||
import com.dogecoin.dogecoinj.core.Wallet;
|
||||
import com.dogecoin.dogecoinj.crypto.TransactionSignature;
|
||||
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -51,7 +54,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
|
||||
protected final Transaction tx;
|
||||
protected final List<Transaction> dependencies;
|
||||
protected final Wallet wallet;
|
||||
protected final @Nullable Wallet wallet;
|
||||
|
||||
private Transaction nonStandard;
|
||||
protected Transaction nonFinal;
|
||||
@ -69,17 +72,20 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
analyzed = true;
|
||||
|
||||
Result result = analyzeIsFinal();
|
||||
if (result != Result.OK)
|
||||
if (result != null && result != Result.OK)
|
||||
return result;
|
||||
|
||||
return analyzeIsStandard();
|
||||
}
|
||||
|
||||
private Result analyzeIsFinal() {
|
||||
private @Nullable 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;
|
||||
|
||||
if (wallet == null)
|
||||
return null;
|
||||
|
||||
final int height = wallet.getLastBlockSeenHeight();
|
||||
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
|
||||
@ -108,7 +114,8 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
VERSION,
|
||||
DUST,
|
||||
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()) {
|
||||
if (chunk.data != null && !chunk.isShortestPossiblePushData())
|
||||
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;
|
||||
}
|
||||
@ -172,7 +192,7 @@ public class DefaultRiskAnalysis implements RiskAnalysis {
|
||||
private Result analyzeIsStandard() {
|
||||
// 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.
|
||||
if (!wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
||||
if (wallet != null && !wallet.getNetworkParameters().getId().equals(NetworkParameters.ID_MAINNET))
|
||||
return Result.OK;
|
||||
|
||||
RuleViolation ruleViolation = isStandard(tx);
|
||||
|
@ -244,6 +244,8 @@ public class MarriedKeyChain extends DeterministicKeyChain {
|
||||
builder.append(script.getToAddress(params));
|
||||
builder.append(" hash160:");
|
||||
builder.append(Utils.HEX.encode(script.getPubKeyHash()));
|
||||
if (script.getCreationTimeSeconds() > 0)
|
||||
builder.append(" creationTimeSeconds:").append(script.getCreationTimeSeconds());
|
||||
builder.append("\n");
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.ECKey.ECDSASignature;
|
||||
import com.dogecoin.dogecoinj.crypto.EncryptedData;
|
||||
import com.dogecoin.dogecoinj.crypto.KeyCrypter;
|
||||
import com.dogecoin.dogecoinj.crypto.KeyCrypterScrypt;
|
||||
@ -96,11 +97,15 @@ public class ECKeyTest {
|
||||
}
|
||||
List<ECKey.ECDSASignature> sigs = Futures.allAsList(sigFutures).get();
|
||||
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);
|
||||
assertEquals(sigs.get(0), duplicate);
|
||||
assertEquals(sigs.get(0).hashCode(), duplicate.hashCode());
|
||||
final ECDSASignature first = sigs.get(0);
|
||||
final ECKey.ECDSASignature duplicate = new ECKey.ECDSASignature(first.r, first.s);
|
||||
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
|
||||
|
@ -17,12 +17,15 @@
|
||||
|
||||
package com.dogecoin.dogecoinj.wallet;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.Arrays;
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.dogecoin.dogecoinj.crypto.TransactionSignature;
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.script.ScriptBuilder;
|
||||
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.dogecoin.dogecoinj.wallet.DefaultRiskAnalysis.RuleViolation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -160,6 +163,22 @@ public class DefaultRiskAnalysisTest {
|
||||
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
|
||||
public void standardOutputs() throws Exception {
|
||||
Transaction tx = new Transaction(params);
|
||||
|
@ -17,35 +17,73 @@
|
||||
|
||||
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.PeerGroup;
|
||||
import com.dogecoin.dogecoinj.core.Transaction;
|
||||
import com.dogecoin.dogecoinj.net.discovery.DnsDiscovery;
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
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.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class WatchMempool {
|
||||
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 {
|
||||
BriefLogFormatter.init();
|
||||
NetworkParameters params = MainNetParams.get();
|
||||
PeerGroup peerGroup = new PeerGroup(params);
|
||||
peerGroup.addPeerDiscovery(new DnsDiscovery(params));
|
||||
PeerGroup peerGroup = new PeerGroup(PARAMS);
|
||||
peerGroup.setMaxConnections(32);
|
||||
peerGroup.addPeerDiscovery(new DnsDiscovery(PARAMS));
|
||||
peerGroup.addEventListener(new AbstractPeerEventListener() {
|
||||
@Override
|
||||
public void onTransaction(Peer peer, Transaction tx) {
|
||||
try {
|
||||
log.info("tx {}", tx.getHash());
|
||||
if (tx.getOutputs().size() != 1) return;
|
||||
if (!tx.getOutput(0).getScriptPubKey().isSentToRawPubKey()) return;
|
||||
log.info("Saw raw pay to pubkey {}", tx);
|
||||
} catch (ScriptException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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));
|
||||
}
|
||||
});
|
||||
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>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<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>
|
||||
</execution>
|
||||
</executions>
|
||||
|
Loading…
x
Reference in New Issue
Block a user