Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
086c52ce12 | |||
d97d867579 | |||
bc02806bce | |||
1bd005fb5e | |||
f68d85cea0 | |||
765b452f7d |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/opretwalletorg.bitcoin.test.wallet
|
/opretwalletorg.bitcoin.test.wallet
|
||||||
|
/README.html
|
||||||
|
|
|
@ -60,6 +60,7 @@ A MK is stored along with the key birthday, which is the date of the first appea
|
||||||
### MVK announce subkey VK 0xECA[1,2] - A-nnounce
|
### MVK announce subkey VK 0xECA[1,2] - A-nnounce
|
||||||
|
|
||||||
nonce[0:32] = nonce[0:16] | nonce[16:32]
|
nonce[0:32] = nonce[0:16] | nonce[16:32]
|
||||||
|
|
||||||
data chunks are prepended with zeros, if its length is smaller than 16.
|
data chunks are prepended with zeros, if its length is smaller than 16.
|
||||||
E.g.
|
E.g.
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ If nonce is missing completely, then
|
||||||
is assumed.
|
is assumed.
|
||||||
|
|
||||||
A nonce **must** be used only once. Either only one VK_pub is announced per MVK ever and nonce is missing,
|
A nonce **must** be used only once. Either only one VK_pub is announced per MVK ever and nonce is missing,
|
||||||
or for every MVK announcement, the nonce has to be *unique* or *true random* bytes.
|
or for every MVK announcement, the nonce has to be **unique** or **true random** bytes.
|
||||||
|
|
||||||
sharedkey = sha256(sha256(MVK_pub | nonce))
|
sharedkey = sha256(sha256(MVK_pub | nonce))
|
||||||
xornonce[24] = sha256(sharedkey | nonce)[0:24]
|
xornonce[24] = sha256(sharedkey | nonce)[0:24]
|
||||||
|
|
3
opret-testapp/.gitignore
vendored
3
opret-testapp/.gitignore
vendored
|
@ -2,3 +2,6 @@
|
||||||
/walletappkit-example.spvchain
|
/walletappkit-example.spvchain
|
||||||
/walletappkit-example.wallet
|
/walletappkit-example.wallet
|
||||||
/opretwalletorg.bitcoin.test.wallet
|
/opretwalletorg.bitcoin.test.wallet
|
||||||
|
/opretwalletorg.bitcoin.production.spvchain
|
||||||
|
/opretwalletorg.bitcoin.production.wallet
|
||||||
|
/opretwalletorg.bitcoin.test.wallet.bak
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=UTF-8
|
encoding//src/main/java=UTF-8
|
||||||
encoding//src/main/resources=UTF-8
|
encoding//src/main/resources=UTF-8
|
||||||
|
encoding//src/test/java=UTF-8
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>4.11</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
<manifest>
|
<manifest>
|
||||||
<addClasspath>true</addClasspath>
|
<addClasspath>true</addClasspath>
|
||||||
<classpathPrefix>lib/</classpathPrefix>
|
<classpathPrefix>lib/</classpathPrefix>
|
||||||
<mainClass>org.tcpid.opretj.testapp.App</mainClass>
|
<mainClass>org.tcpid.opret.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -106,6 +106,16 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- Unit tests plugin, to skip runing test add -Dmaven.test.skip -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.19.1</version>
|
||||||
|
<configuration>
|
||||||
|
<runOrder>alphabetical</runOrder>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
|
|
@ -58,6 +58,10 @@ public class MasterSigningKey extends SigningKey {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
if (this.keyindex.isEmpty()) {
|
||||||
|
return Encoder.HEX.encode(seed);
|
||||||
|
} else {
|
||||||
return "Index: " + this.keyindex.toString() + " " + Encoder.HEX.encode(seed);
|
return "Index: " + this.keyindex.toString() + " " + Encoder.HEX.encode(seed);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.tcpid.key;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.tcpid.opretj.OPRETTransaction;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
|
public class MasterVerifyKey extends VerifyKey {
|
||||||
|
// FIXME: make private again
|
||||||
|
public final LinkedList<MasterVerifyKey> subkeys = new LinkedList<>();
|
||||||
|
|
||||||
|
public MasterVerifyKey(final byte[] key) {
|
||||||
|
super(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSubKeys() {
|
||||||
|
subkeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterVerifyKey getSubKeybyHash(List<Byte> subpkhash) {
|
||||||
|
for (final MasterVerifyKey k : subkeys) {
|
||||||
|
if (Arrays.equals(k.getShortHash(), Bytes.toArray(subpkhash))) {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterVerifyKey getValidSubKey() {
|
||||||
|
for (final MasterVerifyKey k : subkeys) {
|
||||||
|
if (!k.isRevoked()) {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revokeSubKey(final VerifyKey key) {
|
||||||
|
final int i = subkeys.indexOf(key);
|
||||||
|
|
||||||
|
if (i == -1) {
|
||||||
|
throw new NoSuchElementException("No such subkey");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstValidSubKey(final MasterVerifyKey key, final OPRETTransaction t1, final OPRETTransaction t2) {
|
||||||
|
if (!subkeys.isEmpty()) {
|
||||||
|
throw new IndexOutOfBoundsException("Subkey list is not empty");
|
||||||
|
}
|
||||||
|
subkeys.addLast(key);
|
||||||
|
key.setMasterkey(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextValidSubKey(final MasterVerifyKey after, final MasterVerifyKey key, OPRETTransaction t1,
|
||||||
|
OPRETTransaction t2) {
|
||||||
|
|
||||||
|
if (subkeys.contains(key)) {
|
||||||
|
throw new NoSuchElementException("Already in");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int i = subkeys.indexOf(after);
|
||||||
|
|
||||||
|
if (i == -1) {
|
||||||
|
throw new NoSuchElementException("No such subkey");
|
||||||
|
}
|
||||||
|
|
||||||
|
subkeys.add(i + 1, key);
|
||||||
|
key.setMasterkey(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -102,6 +102,9 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
if (this.revoked != true) {
|
if (this.revoked != true) {
|
||||||
this.revoked = revoked;
|
this.revoked = revoked;
|
||||||
}
|
}
|
||||||
|
if (this.masterkey != null) {
|
||||||
|
this.masterkey.revokeSubKey(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] toBytes() {
|
public byte[] toBytes() {
|
||||||
|
@ -114,7 +117,7 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Encoder.HEX.encode(key);
|
return Encoder.HEX.encode(key) + (revoked ? " - Revoked" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verify(final byte[] message, final byte[] signature) {
|
public boolean verify(final byte[] message, final byte[] signature) {
|
||||||
|
@ -124,8 +127,13 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
final int[] bufferLen = new int[1];
|
final int[] bufferLen = new int[1];
|
||||||
|
|
||||||
sodium();
|
sodium();
|
||||||
return Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
try {
|
||||||
|
Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
||||||
"signature was forged or corrupted");
|
"signature was forged or corrupted");
|
||||||
|
return true;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verify(final String message, final String signature, final Encoder encoder) {
|
public boolean verify(final String message, final String signature, final Encoder encoder) {
|
|
@ -1,4 +1,4 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opret;
|
||||||
|
|
||||||
import org.tcpid.key.MasterVerifyKey;
|
import org.tcpid.key.MasterVerifyKey;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opret;
|
||||||
|
|
||||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||||
import static org.libsodium.jni.NaCl.sodium;
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
|
@ -9,9 +9,11 @@ import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
import org.bitcoinj.core.PartialMerkleTree;
|
||||||
|
@ -32,14 +34,17 @@ import org.tcpid.key.MasterSigningKey;
|
||||||
import org.tcpid.key.MasterVerifyKey;
|
import org.tcpid.key.MasterVerifyKey;
|
||||||
import org.tcpid.key.SigningKey;
|
import org.tcpid.key.SigningKey;
|
||||||
import org.tcpid.key.VerifyKey;
|
import org.tcpid.key.VerifyKey;
|
||||||
|
import org.tcpid.opretj.OPRETBaseHandler;
|
||||||
|
import org.tcpid.opretj.OPRETTransaction;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
public class OPRETECParser extends OPRETBaseHandler {
|
public class OPRETECParser extends OPRETBaseHandler {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OPRETECParser.class);
|
private static final Logger logger = LoggerFactory.getLogger(OPRETECParser.class);
|
||||||
public static final Hash HASH = new Hash();
|
|
||||||
|
|
||||||
|
public static final Hash HASH = new Hash();
|
||||||
private static final List<Byte> OPRET_MAGIC_EC1C = Bytes.asList(Utils.HEX.decode("ec1c"));
|
private static final List<Byte> OPRET_MAGIC_EC1C = Bytes.asList(Utils.HEX.decode("ec1c"));
|
||||||
|
|
||||||
private static final List<Byte> OPRET_MAGIC_EC1D = Bytes.asList(Utils.HEX.decode("ec1d"));
|
private static final List<Byte> OPRET_MAGIC_EC1D = Bytes.asList(Utils.HEX.decode("ec1d"));
|
||||||
private static final List<Byte> OPRET_MAGIC_ECA1 = Bytes.asList(Utils.HEX.decode("eca1"));
|
private static final List<Byte> OPRET_MAGIC_ECA1 = Bytes.asList(Utils.HEX.decode("eca1"));
|
||||||
private static final List<Byte> OPRET_MAGIC_ECA2 = Bytes.asList(Utils.HEX.decode("eca2"));
|
private static final List<Byte> OPRET_MAGIC_ECA2 = Bytes.asList(Utils.HEX.decode("eca2"));
|
||||||
|
@ -57,7 +62,7 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
logger.debug("Using VerifyKey {}", k);
|
logger.debug("Using VerifyKey {}", k);
|
||||||
|
|
||||||
if (k.verify(revokemsg, sig)) {
|
if (k.verify(revokemsg, sig)) {
|
||||||
logger.debug("REVOKED VerifyKey {}", k);
|
logger.debug("REVOKED Key {}", k.toString());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
logger.debug("SIGNATURE does not match!");
|
logger.debug("SIGNATURE does not match!");
|
||||||
|
@ -65,6 +70,60 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Script[] getAnnounceFirstScript(final MasterSigningKey msk, final MasterVerifyKey subkey) {
|
||||||
|
logger.debug("getAnnounceFirstScript");
|
||||||
|
final MasterVerifyKey mvk = msk.getMasterVerifyKey();
|
||||||
|
final byte[] sig = msk.sign(subkey.toBytes());
|
||||||
|
|
||||||
|
logger.debug("using key {}", Encoder.HEX.encode(mvk.toBytes()));
|
||||||
|
final byte[] noncebytes = Util.zeros(32);
|
||||||
|
final byte[] sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(mvk.toBytes(), noncebytes)));
|
||||||
|
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
||||||
|
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||||
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
|
||||||
|
final byte[] cipher = Util.zeros(96);
|
||||||
|
final byte[] msg = Bytes.concat(subkey.toBytes(), sig);
|
||||||
|
|
||||||
|
sodium();
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(cipher, msg, 96, xornonce, sharedkey);
|
||||||
|
|
||||||
|
final Script script1 = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("eca1"))
|
||||||
|
.data(Arrays.copyOfRange(cipher, 0, 48)).data(mvk.getShortHash()).build();
|
||||||
|
final Script script2 = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("eca2"))
|
||||||
|
.data(Arrays.copyOfRange(cipher, 48, 96)).data(mvk.getShortHash()).build();
|
||||||
|
final Script[] scripts = { script1, script2 };
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Script[] getAnnounceNextScript(final MasterSigningKey msk, final MasterVerifyKey prev,
|
||||||
|
final MasterVerifyKey next) {
|
||||||
|
logger.debug("getAnnounceNextScript");
|
||||||
|
|
||||||
|
final MasterVerifyKey mvk = msk.getMasterVerifyKey();
|
||||||
|
|
||||||
|
final byte[] sig = msk.sign(next.toBytes());
|
||||||
|
|
||||||
|
logger.debug("using key {}", Encoder.HEX.encode(prev.toBytes()));
|
||||||
|
final byte[] sharedkey = HASH.sha256(HASH.sha256(prev.toBytes()));
|
||||||
|
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(sharedkey), 0, 24);
|
||||||
|
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||||
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
|
||||||
|
final byte[] cipher = Util.zeros(96);
|
||||||
|
final byte[] msg = Bytes.concat(next.toBytes(), sig);
|
||||||
|
|
||||||
|
sodium();
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(cipher, msg, 96, xornonce, sharedkey);
|
||||||
|
|
||||||
|
final Script script1 = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("eca3"))
|
||||||
|
.data(Arrays.copyOfRange(cipher, 0, 48)).data(mvk.getShortHash()).data(prev.getShortHash()).build();
|
||||||
|
final Script script2 = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("eca4"))
|
||||||
|
.data(Arrays.copyOfRange(cipher, 48, 96)).data(mvk.getShortHash()).data(prev.getShortHash()).build();
|
||||||
|
final Script[] scripts = { script1, script2 };
|
||||||
|
return scripts;
|
||||||
|
}
|
||||||
|
|
||||||
public static Script getRevokeScript(final SigningKey key) {
|
public static Script getRevokeScript(final SigningKey key) {
|
||||||
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), key.getVerifyKey().toHash());
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), key.getVerifyKey().toHash());
|
||||||
final byte[] sig = key.sign(revokemsg);
|
final byte[] sig = key.sign(revokemsg);
|
||||||
|
@ -74,9 +133,12 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
return script;
|
return script;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean needscan;
|
||||||
|
|
||||||
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = Collections.synchronizedMap(new HashMap<>());
|
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = Collections.synchronizedMap(new HashMap<>());
|
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
protected final Map<List<Byte>, List<OPRETTransaction>> transA1HashMap = Collections
|
protected final Map<List<Byte>, List<OPRETTransaction>> transA1HashMap = Collections
|
||||||
.synchronizedMap(new HashMap<>());
|
.synchronizedMap(new HashMap<>());
|
||||||
protected final Map<List<Byte>, List<OPRETTransaction>> transA2HashMap = Collections
|
protected final Map<List<Byte>, List<OPRETTransaction>> transA2HashMap = Collections
|
||||||
|
@ -89,11 +151,15 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
.synchronizedMap(new HashMap<>());
|
.synchronizedMap(new HashMap<>());
|
||||||
protected final Map<List<Byte>, List<OPRETTransaction>> trans52HashMap = Collections
|
protected final Map<List<Byte>, List<OPRETTransaction>> trans52HashMap = Collections
|
||||||
.synchronizedMap(new HashMap<>());
|
.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
protected final Map<List<Byte>, List<MasterVerifyKey>> verifyKeys = Collections.synchronizedMap(new HashMap<>());
|
protected final Map<List<Byte>, List<MasterVerifyKey>> verifyKeys = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public OPRETECParser() {
|
||||||
|
super();
|
||||||
|
needscan = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an event listener object. Methods on this object are called when
|
* Adds an event listener object. Methods on this object are called when
|
||||||
* scripts watched by this wallet change. The listener is executed by the
|
* scripts watched by this wallet change. The listener is executed by the
|
||||||
|
@ -111,8 +177,9 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyKeys.get(hash).add(key);
|
verifyKeys.get(hash).add(key);
|
||||||
logger.debug("Adding pkhash {}", key.getShortHash());
|
logger.debug("Adding pkhash {}", Utils.HEX.encode(key.getShortHash()));
|
||||||
addOPRET(key.getShortHash(), earliestTime);
|
addOPRET(key.getShortHash(), earliestTime);
|
||||||
|
needscan = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean cryptoSelfTest() {
|
public boolean cryptoSelfTest() {
|
||||||
|
@ -163,6 +230,189 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean handleAnnounce(final OPRETTransaction selfTx,
|
||||||
|
final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap,
|
||||||
|
final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) {
|
||||||
|
logger.debug("handleAnnounce");
|
||||||
|
final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1));
|
||||||
|
if (((selfData.length < 48) || (selfData.length > 64))) {
|
||||||
|
logger.debug("invalid chunk1 size = {}", selfData.length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Byte> pkhash = selfTx.opretData.get(2);
|
||||||
|
if (pkhash.size() != 12) {
|
||||||
|
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifyKeys.containsKey(pkhash)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherTransHashMap.containsKey(pkhash)) {
|
||||||
|
for (final OPRETTransaction otherTx : otherTransHashMap.get(pkhash)) {
|
||||||
|
final byte[] otherData = Bytes.toArray(otherTx.opretData.get(1));
|
||||||
|
final byte[] cipher = isT1
|
||||||
|
? Bytes.concat(Arrays.copyOfRange(selfData, 0, 48), Arrays.copyOfRange(otherData, 0, 48))
|
||||||
|
: Bytes.concat(Arrays.copyOfRange(otherData, 0, 48), Arrays.copyOfRange(selfData, 0, 48));
|
||||||
|
final BigInteger selfNonce = (selfData.length == 48) ? BigInteger.ZERO
|
||||||
|
: new BigInteger(1, Arrays.copyOfRange(selfData, 48, selfData.length));
|
||||||
|
final BigInteger otherNonce = (otherData.length == 48) ? BigInteger.ZERO
|
||||||
|
: new BigInteger(1, Arrays.copyOfRange(otherData, 48, otherData.length));
|
||||||
|
|
||||||
|
final BigInteger nonce = isT1 ? selfNonce.shiftLeft(16 * 8).or(otherNonce)
|
||||||
|
: otherNonce.shiftLeft(16 * 8).or(selfNonce);
|
||||||
|
|
||||||
|
byte[] noncebytes = Util.prependZeros(32, nonce.toByteArray());
|
||||||
|
noncebytes = Arrays.copyOfRange(noncebytes, noncebytes.length - 32, noncebytes.length);
|
||||||
|
|
||||||
|
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||||
|
byte[] sharedkey, xornonce;
|
||||||
|
sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(k.toBytes(), noncebytes)));
|
||||||
|
xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
||||||
|
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
|
||||||
|
logger.debug("noncebytes {}", Encoder.HEX.encode(noncebytes));
|
||||||
|
logger.debug("noncebytes len {}", noncebytes.length);
|
||||||
|
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||||
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
sodium();
|
||||||
|
final byte[] msg = Util.zeros(96);
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||||
|
final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
|
||||||
|
final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
|
||||||
|
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
|
||||||
|
|
||||||
|
if (!k.verify(vk, sig)) {
|
||||||
|
logger.debug("sig does not match");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("sig matches");
|
||||||
|
final MasterVerifyKey subkey = new MasterVerifyKey(vk);
|
||||||
|
try {
|
||||||
|
k.setFirstValidSubKey(subkey, isT1 ? selfTx : otherTx, isT1 ? otherTx : selfTx);
|
||||||
|
final Date time = selfTx.getTime();
|
||||||
|
if (time != null) {
|
||||||
|
this.addVerifyKey(subkey, time.getTime() / 1000);
|
||||||
|
}
|
||||||
|
needscan = true;
|
||||||
|
logger.info("MVK {} announced first subkey {}", k.toString(), subkey.toString());
|
||||||
|
} catch (final IndexOutOfBoundsException e) {
|
||||||
|
logger.info("FAILED: MVK {} announced first key {}, but it was already announced", k.toString(),
|
||||||
|
subkey.toString());
|
||||||
|
}
|
||||||
|
otherTransHashMap.get(pkhash).remove(otherTx);
|
||||||
|
if (otherTransHashMap.get(pkhash).isEmpty()) {
|
||||||
|
otherTransHashMap.remove(pkhash);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no matching transaction found, save for later
|
||||||
|
if (!selfTransHashMap.containsKey(pkhash)) {
|
||||||
|
selfTransHashMap.put(pkhash, new ArrayList<OPRETTransaction>());
|
||||||
|
}
|
||||||
|
selfTransHashMap.get(pkhash).add(selfTx);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleAnnounceNext(final OPRETTransaction selfTx,
|
||||||
|
final Map<List<Byte>, List<OPRETTransaction>> selfTransHashMap,
|
||||||
|
final Map<List<Byte>, List<OPRETTransaction>> otherTransHashMap, final boolean isT1) {
|
||||||
|
|
||||||
|
logger.debug("handleAnnounceNext");
|
||||||
|
final byte[] selfData = Bytes.toArray(selfTx.opretData.get(1));
|
||||||
|
if (selfData.length != 48) {
|
||||||
|
logger.debug("invalid chunk1 size = {}", selfData.length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Byte> pkhash = selfTx.opretData.get(2);
|
||||||
|
if (pkhash.size() != 12) {
|
||||||
|
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Byte> subpkhash = selfTx.opretData.get(3);
|
||||||
|
if (subpkhash.size() != 12) {
|
||||||
|
logger.debug("chunk 2 size != 12 but {} ", subpkhash.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifyKeys.containsKey(pkhash)) {
|
||||||
|
logger.debug("!verifyKeys.containsKey(pkhash)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherTransHashMap.containsKey(pkhash)) {
|
||||||
|
for (final OPRETTransaction otherTx : otherTransHashMap.get(pkhash)) {
|
||||||
|
final byte[] otherData = Bytes.toArray(otherTx.opretData.get(1));
|
||||||
|
final byte[] cipher = isT1 ? Bytes.concat(selfData, otherData) : Bytes.concat(otherData, selfData);
|
||||||
|
|
||||||
|
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||||
|
final MasterVerifyKey vk_n = k.getSubKeybyHash(subpkhash);
|
||||||
|
if (vk_n == null) {
|
||||||
|
logger.debug("! k.getSubKeybyHash(subpkhash)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] sharedkey = HASH.sha256(vk_n.toHash());
|
||||||
|
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(sharedkey), 0, 24);
|
||||||
|
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
|
||||||
|
logger.debug("checking subkey {}", Encoder.HEX.encode(vk_n.toBytes()));
|
||||||
|
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||||
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
sodium();
|
||||||
|
final byte[] msg = Util.zeros(96);
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||||
|
final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
|
||||||
|
final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
|
||||||
|
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
|
||||||
|
|
||||||
|
if (!k.verify(vk, sig)) {
|
||||||
|
logger.debug("sig does not match");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("sig matches");
|
||||||
|
final MasterVerifyKey subkey = new MasterVerifyKey(vk);
|
||||||
|
try {
|
||||||
|
k.setNextValidSubKey(vk_n, subkey, isT1 ? selfTx : otherTx, isT1 ? otherTx : selfTx);
|
||||||
|
final Date time = selfTx.getTime();
|
||||||
|
if (time != null) {
|
||||||
|
this.addVerifyKey(subkey, time.getTime() / 1000);
|
||||||
|
}
|
||||||
|
needscan = true;
|
||||||
|
logger.info("MKV {} announced next subkey:\n\t{} -> {}", k.toString(), vk_n.toString(),
|
||||||
|
subkey.toString());
|
||||||
|
} catch (final NoSuchElementException e) {
|
||||||
|
logger.info("FAILED or duplicate: MKV {} announced next subkey:\n\t{} -> {}", k.toString(),
|
||||||
|
vk_n.toString(), subkey.toString());
|
||||||
|
}
|
||||||
|
otherTransHashMap.get(pkhash).remove(otherTx);
|
||||||
|
if (otherTransHashMap.get(pkhash).isEmpty()) {
|
||||||
|
otherTransHashMap.remove(pkhash);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("!otherTransHashMap.containsKey(pkhash)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// no matching transaction found, save for later
|
||||||
|
if (!selfTransHashMap.containsKey(pkhash)) {
|
||||||
|
selfTransHashMap.put(pkhash, new ArrayList<OPRETTransaction>());
|
||||||
|
}
|
||||||
|
selfTransHashMap.get(pkhash).add(selfTx);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean handleEC0F(final OPRETTransaction t) {
|
private boolean handleEC0F(final OPRETTransaction t) {
|
||||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||||
if ((sig.length != 64)) {
|
if ((sig.length != 64)) {
|
||||||
|
@ -183,11 +433,11 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
||||||
if (checkKeyforRevoke(k, sig)) {
|
if (checkKeyforRevoke(k, sig)) {
|
||||||
if (k.isRevoked()) {
|
if (k.isRevoked()) {
|
||||||
logger.debug("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
logger.info("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
||||||
Utils.HEX.encode(sig));
|
Utils.HEX.encode(sig));
|
||||||
} else {
|
} else {
|
||||||
k.setRevoked(true);
|
k.setRevoked(true);
|
||||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
logger.info("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
||||||
}
|
}
|
||||||
queueOnOPRETRevoke(k);
|
queueOnOPRETRevoke(k);
|
||||||
return true;
|
return true;
|
||||||
|
@ -203,6 +453,7 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleEC1D(final OPRETTransaction t) {
|
private boolean handleEC1D(final OPRETTransaction t) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||||
if ((sig.length != 64)) {
|
if ((sig.length != 64)) {
|
||||||
logger.debug("chunk 1 size != 64, but {}", sig.length);
|
logger.debug("chunk 1 size != 64, but {}", sig.length);
|
||||||
|
@ -219,20 +470,18 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
// FIXME
|
||||||
if (checkKeyforRevoke(k, sig)) {
|
/*
|
||||||
if (k.isRevoked()) {
|
* for (final MasterVerifyKey k : verifyKeys.get(pkhash)) { if
|
||||||
logger.debug("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
* (checkKeyforRevoke(k, sig)) { if (k.isRevoked()) {
|
||||||
Utils.HEX.encode(sig));
|
* logger.debug("Duplicate REVOKE PK {} - SIG {}",
|
||||||
} else {
|
* Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig)); } else {
|
||||||
k.setRevoked(true);
|
* k.setRevoked(true); logger.debug("REVOKE PK {} - SIG {}",
|
||||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
* Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig)); }
|
||||||
}
|
* queueOnOPRETId(k); return true;
|
||||||
queueOnOPRETId(k);
|
*
|
||||||
return true;
|
* } }
|
||||||
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,179 +496,35 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleECA1(final OPRETTransaction t1) {
|
private boolean handleECA1(final OPRETTransaction t1) {
|
||||||
// FIXME: refactor with handleECA2
|
|
||||||
|
|
||||||
logger.debug("handleECA1");
|
logger.debug("handleECA1");
|
||||||
final byte[] data1 = Bytes.toArray(t1.opretData.get(1));
|
return handleAnnounce(t1, transA1HashMap, transA2HashMap, true);
|
||||||
if (((data1.length < 48) || (data1.length > 64))) {
|
|
||||||
logger.debug("invalid chunk1 size = {}", data1.length);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Byte> pkhash = t1.opretData.get(2);
|
|
||||||
if ((pkhash.size() != 12)) {
|
|
||||||
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verifyKeys.containsKey(pkhash)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transA2HashMap.containsKey(pkhash)) {
|
|
||||||
for (final OPRETTransaction t2 : transA2HashMap.get(pkhash)) {
|
|
||||||
final byte[] data2 = Bytes.toArray(t2.opretData.get(1));
|
|
||||||
final byte[] cipher = Bytes.concat(Arrays.copyOfRange(data1, 0, 48), Arrays.copyOfRange(data2, 0, 48));
|
|
||||||
BigInteger nonce1 = BigInteger.ZERO;
|
|
||||||
BigInteger nonce2 = BigInteger.ZERO;
|
|
||||||
if (data1.length > 48) {
|
|
||||||
nonce1 = new BigInteger(1, Arrays.copyOfRange(data1, 48, data1.length));
|
|
||||||
logger.debug("nonce1 {}", Encoder.HEX.encode(nonce1.toByteArray()));
|
|
||||||
logger.debug("nonce1shift {}", Encoder.HEX.encode(nonce1.shiftLeft(16 * 8).toByteArray()));
|
|
||||||
}
|
|
||||||
if (data2.length > 48) {
|
|
||||||
nonce2 = new BigInteger(1, Arrays.copyOfRange(data2, 48, data2.length));
|
|
||||||
logger.debug("nonce2 {}", Encoder.HEX.encode(nonce2.toByteArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
final BigInteger nonce = nonce1.shiftLeft(16 * 8).or(nonce2);
|
|
||||||
logger.debug("nonceshift {}", Encoder.HEX.encode(nonce.toByteArray()));
|
|
||||||
|
|
||||||
byte[] noncebytes = Util.prependZeros(32, nonce.toByteArray());
|
|
||||||
noncebytes = Arrays.copyOfRange(noncebytes, noncebytes.length - 32, noncebytes.length);
|
|
||||||
|
|
||||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
|
||||||
byte[] sharedkey, xornonce;
|
|
||||||
sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(k.toBytes(), noncebytes)));
|
|
||||||
xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
|
||||||
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
|
|
||||||
logger.debug("noncebytes {}", Encoder.HEX.encode(noncebytes));
|
|
||||||
logger.debug("noncebytes len {}", noncebytes.length);
|
|
||||||
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
|
||||||
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
|
||||||
sodium();
|
|
||||||
final byte[] msg = Util.zeros(96);
|
|
||||||
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
|
||||||
final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
|
|
||||||
final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
|
|
||||||
try {
|
|
||||||
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
|
|
||||||
k.verify(vk, sig);
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
logger.debug("sig does not match");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logger.debug("sig matches");
|
|
||||||
|
|
||||||
k.setFirstValidSubKey(new MasterVerifyKey(vk), t1, t2);
|
|
||||||
transA2HashMap.get(pkhash).remove(t2);
|
|
||||||
if (transA2HashMap.get(pkhash).isEmpty()) {
|
|
||||||
transA2HashMap.remove(pkhash);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!transA1HashMap.containsKey(pkhash)) {
|
|
||||||
transA1HashMap.put(pkhash, new ArrayList<OPRETTransaction>());
|
|
||||||
}
|
|
||||||
transA1HashMap.get(pkhash).add(t1);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleECA2(final OPRETTransaction t2) {
|
private boolean handleECA2(final OPRETTransaction t2) {
|
||||||
// FIXME: refactor with handleECA1
|
|
||||||
logger.debug("handleECA2");
|
logger.debug("handleECA2");
|
||||||
final byte[] data2 = Bytes.toArray(t2.opretData.get(1));
|
return handleAnnounce(t2, transA2HashMap, transA1HashMap, false);
|
||||||
if (((data2.length < 48) || (data2.length > 64))) {
|
|
||||||
logger.debug("invalid chunk1 size = {}", data2.length);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Byte> pkhash = t2.opretData.get(2);
|
private boolean handleECA3(final OPRETTransaction t1) {
|
||||||
if ((pkhash.size() != 12)) {
|
logger.debug("handleECA3");
|
||||||
logger.debug("chunk 2 size != 12 but {} ", pkhash.size());
|
return handleAnnounceNext(t1, transA3HashMap, transA4HashMap, true);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!verifyKeys.containsKey(pkhash)) {
|
private boolean handleECA4(final OPRETTransaction t2) {
|
||||||
logger.debug("pkash not in hashmap");
|
logger.debug("handleECA4");
|
||||||
return false;
|
return handleAnnounceNext(t2, transA4HashMap, transA3HashMap, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transA1HashMap.containsKey(pkhash)) {
|
public boolean needScan() {
|
||||||
for (final OPRETTransaction t1 : transA1HashMap.get(pkhash)) {
|
|
||||||
final byte[] data1 = Bytes.toArray(t1.opretData.get(1));
|
|
||||||
final byte[] cipher = Bytes.concat(Arrays.copyOfRange(data1, 0, 48), Arrays.copyOfRange(data2, 0, 48));
|
|
||||||
BigInteger nonce1 = BigInteger.ZERO;
|
|
||||||
BigInteger nonce2 = BigInteger.ZERO;
|
|
||||||
if (data1.length > 48) {
|
|
||||||
nonce1 = new BigInteger(1, Arrays.copyOfRange(data1, 48, data1.length));
|
|
||||||
}
|
|
||||||
if (data2.length > 48) {
|
|
||||||
nonce2 = new BigInteger(1, Arrays.copyOfRange(data2, 48, data2.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
final BigInteger nonce = nonce1.shiftLeft(16 * 8).or(nonce2);
|
|
||||||
byte[] noncebytes = Util.prependZeros(32, nonce.toByteArray());
|
|
||||||
noncebytes = Arrays.copyOfRange(noncebytes, noncebytes.length - 32, noncebytes.length);
|
|
||||||
|
|
||||||
for (final MasterVerifyKey k : verifyKeys.get(pkhash)) {
|
|
||||||
byte[] sharedkey, xornonce;
|
|
||||||
logger.debug("checking key {}", Encoder.HEX.encode(k.toBytes()));
|
|
||||||
logger.debug("noncebytes {}", Encoder.HEX.encode(noncebytes));
|
|
||||||
logger.debug("noncebytes len {}", noncebytes.length);
|
|
||||||
sharedkey = HASH.sha256(HASH.sha256(Bytes.concat(k.toBytes(), noncebytes)));
|
|
||||||
xornonce = Arrays.copyOfRange(HASH.sha256(Bytes.concat(sharedkey, noncebytes)), 0, 24);
|
|
||||||
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
|
||||||
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
|
||||||
|
|
||||||
sodium();
|
|
||||||
final byte[] msg = Util.zeros(96);
|
|
||||||
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
|
||||||
final byte[] vk = Arrays.copyOfRange(msg, 0, 32);
|
|
||||||
final byte[] sig = Arrays.copyOfRange(msg, 32, 96);
|
|
||||||
try {
|
|
||||||
logger.debug("Checking sig {} with key {}", Encoder.HEX.encode(sig), Encoder.HEX.encode(vk));
|
|
||||||
k.verify(vk, sig);
|
|
||||||
} catch (final RuntimeException e) {
|
|
||||||
logger.debug("sig does not match");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("sig matches");
|
|
||||||
k.setFirstValidSubKey(new MasterVerifyKey(vk), t1, t2);
|
|
||||||
transA1HashMap.get(pkhash).remove(t1);
|
|
||||||
if (transA1HashMap.get(pkhash).isEmpty()) {
|
|
||||||
transA1HashMap.remove(pkhash);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!transA2HashMap.containsKey(pkhash)) {
|
|
||||||
transA2HashMap.put(pkhash, new ArrayList<OPRETTransaction>());
|
|
||||||
}
|
|
||||||
transA2HashMap.get(pkhash).add(t2);
|
|
||||||
logger.debug("nothing in A1 HashMap");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean handleECA3(final OPRETTransaction t) {
|
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return false;
|
return this.needscan;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleECA4(final OPRETTransaction t) {
|
public boolean pushTransaction(final OPRETTransaction t) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean handleTransaction(final OPRETTransaction t) {
|
|
||||||
logger.debug("checking {}", t.opretData);
|
logger.debug("checking {}", t.opretData);
|
||||||
|
|
||||||
if ((t.opretData.size() != 2) && (t.opretData.size() != 3) && (t.opretData.size() != 4)) {
|
if ((t.opretData.size() != 2) && (t.opretData.size() != 3) && (t.opretData.size() != 4)) {
|
||||||
|
logger.debug("Wrong chunk count");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,8 +570,17 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pushTransaction(final OPRETTransaction t) {
|
public void pushTransactions(List<OPRETTransaction> pushlist) {
|
||||||
handleTransaction(t);
|
Collections.sort(pushlist, (a, b) -> {
|
||||||
|
final List<Byte> chunka = a.opretData.get(0);
|
||||||
|
final List<Byte> chunkb = b.opretData.get(0);
|
||||||
|
final Long la = ((long) chunka.get(1) * 256) + (long) chunka.get(0);
|
||||||
|
final Long lb = ((long) chunkb.get(1) * 256) + (long) chunkb.get(0);
|
||||||
|
return la.compareTo(lb);
|
||||||
|
});
|
||||||
|
for (final OPRETTransaction t : pushlist) {
|
||||||
|
pushTransaction(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueOnOPRETId(final MasterVerifyKey k) {
|
private void queueOnOPRETId(final MasterVerifyKey k) {
|
||||||
|
@ -499,4 +613,9 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
|
|
||||||
removeOPRET(key.getShortHash());
|
removeOPRET(key.getShortHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void willScan() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
this.needscan = false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,12 +7,14 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.AddressFormatException;
|
import org.bitcoinj.core.AddressFormatException;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
import org.bitcoinj.core.InsufficientMoneyException;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
|
@ -22,7 +24,8 @@ import org.libsodium.jni.crypto.Hash;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.tcpid.key.MasterSigningKey;
|
import org.tcpid.key.MasterSigningKey;
|
||||||
import org.tcpid.opretj.OPRETECParser;
|
import org.tcpid.key.MasterVerifyKey;
|
||||||
|
import org.tcpid.opret.OPRETECParser;
|
||||||
import org.tcpid.opretj.OPRETWallet;
|
import org.tcpid.opretj.OPRETWallet;
|
||||||
import org.tcpid.opretj.OPRETWalletAppKit;
|
import org.tcpid.opretj.OPRETWalletAppKit;
|
||||||
|
|
||||||
|
@ -40,8 +43,18 @@ public class App {
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
||||||
private final static MasterSigningKey SK = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
private final static MasterSigningKey SK = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
|
public final static LinkedList<MasterVerifyKey> WATCHKEYS = new LinkedList<>();
|
||||||
|
public static OPRETWalletAppKit KIT;
|
||||||
|
public static NetworkParameters PARAMS;
|
||||||
|
public final static OPRETECParser PARSER = new OPRETECParser();
|
||||||
|
public static String WALLETNAME;
|
||||||
|
|
||||||
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
|
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
|
||||||
|
if (kit == null) {
|
||||||
|
out.println("Need blockchain scan");
|
||||||
|
out.flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
out.write("Balance: " + kit.wallet().getBalance().toFriendlyString() + "\n");
|
out.write("Balance: " + kit.wallet().getBalance().toFriendlyString() + "\n");
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
@ -51,10 +64,17 @@ public class App {
|
||||||
out.write("\n");
|
out.write("\n");
|
||||||
out.write("help - this screen\n");
|
out.write("help - this screen\n");
|
||||||
out.write("quit - exit the application\n");
|
out.write("quit - exit the application\n");
|
||||||
|
out.write("scan - scan the blockchain\n");
|
||||||
out.write("balance - show your available balance\n");
|
out.write("balance - show your available balance\n");
|
||||||
out.write("receive - display an address to receive coins\n");
|
out.write("receive - display an address to receive coins\n");
|
||||||
out.write("empty <address> - send all coins to the address\n");
|
out.write("empty <address> - send all coins to the address\n");
|
||||||
out.write("opret - send opret\n");
|
out.write("\n");
|
||||||
|
out.write("newkey <hashseed> - create a new key with seed = sha256(hashseed)\n");
|
||||||
|
out.write("listsub <index> <key> - list the subkey of <key> with optional <index>\n");
|
||||||
|
out.write("revoke <key> - revoke a key on the blockchain\n");
|
||||||
|
out.write("announce <index> <master> - announce the subkey <index> signed with its <master> key\n");
|
||||||
|
out.write("watch <key> - listen on the blockchain for all actions on <key>\n");
|
||||||
|
out.write("listwatch <key> - listen on the blockchain for all actions on <key>\n");
|
||||||
out.write("\n");
|
out.write("\n");
|
||||||
|
|
||||||
out.flush();
|
out.flush();
|
||||||
|
@ -83,15 +103,16 @@ public class App {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleConsole(final OPRETWalletAppKit kit) throws IOException {
|
private static void handleConsole() throws IOException {
|
||||||
final ConsoleReader reader = new ConsoleReader();
|
final ConsoleReader reader = new ConsoleReader();
|
||||||
final String[] cmds = { "help", "quit", "exit", "balance", "receive", "empty", "opret" };
|
final String[] cmds = { "help", "quit", "exit", "balance", "receive", "empty", "opret", "newkey", "listsub",
|
||||||
|
"revoke", "announce", "listen", "listwatch", "scan", "watch" };
|
||||||
reader.addCompleter(new StringsCompleter(cmds));
|
reader.addCompleter(new StringsCompleter(cmds));
|
||||||
final PrintWriter out = new PrintWriter(reader.getOutput());
|
final PrintWriter out = new PrintWriter(reader.getOutput());
|
||||||
reader.setPrompt("opret> ");
|
reader.setPrompt("opret> ");
|
||||||
String line;
|
String line;
|
||||||
displayHelp(out);
|
displayHelp(out);
|
||||||
displayBalance(kit, out);
|
displayBalance(KIT, out);
|
||||||
|
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
String[] argv = line.split("\\s");
|
String[] argv = line.split("\\s");
|
||||||
|
@ -108,15 +129,16 @@ public class App {
|
||||||
|
|
||||||
switch (cmd.toLowerCase()) {
|
switch (cmd.toLowerCase()) {
|
||||||
case "quit":
|
case "quit":
|
||||||
|
case "exit":
|
||||||
return;
|
return;
|
||||||
case "help":
|
case "help":
|
||||||
displayHelp(out);
|
displayHelp(out);
|
||||||
break;
|
break;
|
||||||
case "balance":
|
case "balance":
|
||||||
displayBalance(kit, out);
|
displayBalance(KIT, out);
|
||||||
break;
|
break;
|
||||||
case "receive":
|
case "receive":
|
||||||
final String receiveStr = kit.wallet().freshReceiveAddress().toString();
|
final String receiveStr = KIT.wallet().freshReceiveAddress().toString();
|
||||||
|
|
||||||
out.write("send money to: " + receiveStr + "\n");
|
out.write("send money to: " + receiveStr + "\n");
|
||||||
try {
|
try {
|
||||||
|
@ -134,9 +156,9 @@ public class App {
|
||||||
out.println("'" + argv[0] + "'");
|
out.println("'" + argv[0] + "'");
|
||||||
out.flush();
|
out.flush();
|
||||||
try {
|
try {
|
||||||
final SendRequest request = SendRequest.emptyWallet(Address.fromBase58(kit.params(), argv[0]));
|
final SendRequest request = SendRequest.emptyWallet(Address.fromBase58(KIT.params(), argv[0]));
|
||||||
try {
|
try {
|
||||||
kit.wallet().sendCoins(request);
|
KIT.wallet().sendCoins(request);
|
||||||
} catch (final InsufficientMoneyException e) {
|
} catch (final InsufficientMoneyException e) {
|
||||||
out.println(e.getLocalizedMessage());
|
out.println(e.getLocalizedMessage());
|
||||||
out.flush();
|
out.flush();
|
||||||
|
@ -146,37 +168,131 @@ public class App {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "opret":
|
case "newkey": {
|
||||||
sendOPReturn(kit, out);
|
if (argv.length != 1) {
|
||||||
|
out.println("'newkey <hash>' needs a an argument");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final byte[] seed = Sha256Hash.of(argv[0].getBytes()).getBytes();
|
||||||
|
final MasterSigningKey key = new MasterSigningKey(seed);
|
||||||
|
out.println("Private: " + key.toString());
|
||||||
|
out.println("Public: " + key.getMasterVerifyKey().toString());
|
||||||
|
out.println("Sha256(pub): " + Utils.HEX.encode(key.getMasterVerifyKey().toHash()));
|
||||||
|
out.println("Sha256(pub)[0:12]: " + Utils.HEX.encode(key.getMasterVerifyKey().getShortHash()));
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "listsub": {
|
||||||
|
if (argv.length != 2) {
|
||||||
|
out.println("'listsub <index> <key>' needs two arguments");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Long index = new Long(argv[0]);
|
||||||
|
final MasterSigningKey mk = new MasterSigningKey(Utils.HEX.decode(argv[1]));
|
||||||
|
final MasterSigningKey key = mk.getSubKey(index);
|
||||||
|
out.println("Private: " + key.toString());
|
||||||
|
out.println("Public: " + key.getMasterVerifyKey().toString());
|
||||||
|
out.println("Sha256(pub): " + Utils.HEX.encode(key.getMasterVerifyKey().toHash()));
|
||||||
|
out.println("Sha256(pub)[0:12]: " + Utils.HEX.encode(key.getMasterVerifyKey().getShortHash()));
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "announce": {
|
||||||
|
if (argv.length != 2) {
|
||||||
|
out.println("'announce <index> <key>' needs two arguments");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Long index = new Long(argv[0]);
|
||||||
|
final MasterSigningKey mk = new MasterSigningKey(Utils.HEX.decode(argv[1]));
|
||||||
|
if (index == 0L) {
|
||||||
|
final MasterVerifyKey subkey = mk.getSubKey(index).getMasterVerifyKey();
|
||||||
|
if (!sendAnnounceFirst(mk, subkey, KIT, out)) {
|
||||||
|
out.println("announce failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final MasterVerifyKey prev = mk.getSubKey(index - 1L).getMasterVerifyKey();
|
||||||
|
final MasterVerifyKey next = mk.getSubKey(index).getMasterVerifyKey();
|
||||||
|
if (!sendAnnounceNext(mk, prev, next, KIT, out)) {
|
||||||
|
out.println("announce failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "revoke":
|
||||||
|
if (argv.length != 1) {
|
||||||
|
out.println("'revoke <key>' needs a an argument");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final MasterSigningKey mk = new MasterSigningKey(Utils.HEX.decode(argv[0]));
|
||||||
|
if (!sendOPReturn(mk, KIT, out)) {
|
||||||
|
out.println("revoke failed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "watch": {
|
||||||
|
if (argv.length != 1) {
|
||||||
|
out.println("'watch <key>' needs a an argument");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final MasterVerifyKey m = new MasterVerifyKey(Utils.HEX.decode(argv[0]));
|
||||||
|
if (!WATCHKEYS.contains(m)) {
|
||||||
|
WATCHKEYS.add(m);
|
||||||
|
PARSER.addVerifyKey(m, OPRET_BIRTHDAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "scan": {
|
||||||
|
scanBlockchain();
|
||||||
|
}
|
||||||
|
// break; Fall through
|
||||||
|
case "listwatch":
|
||||||
|
if (!WATCHKEYS.isEmpty()) {
|
||||||
|
out.println("\n");
|
||||||
|
out.println("Watching Keys:");
|
||||||
|
for (final MasterVerifyKey m : WATCHKEYS) {
|
||||||
|
out.println("\tKey: " + m.toString());
|
||||||
|
out.println("\t\tSubKeys:");
|
||||||
|
|
||||||
|
for (final MasterVerifyKey k : m.subkeys) {
|
||||||
|
out.println("\t\t" + k.toString());
|
||||||
|
}
|
||||||
|
out.println("\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
out.println("Unknown command. Use 'help' to display available commands.");
|
out.println("Unknown command. Use 'help' to display available commands.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String[] args) throws Exception {
|
public static void main(final String[] args) throws Exception {
|
||||||
|
|
||||||
final OptionParser parser = new OptionParser();
|
final OptionParser optparser = new OptionParser();
|
||||||
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
final OptionSpec<NetworkEnum> net = optparser.accepts("net", "The network to run on").withRequiredArg()
|
||||||
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
.ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
||||||
parser.accepts("help", "Displays program options");
|
|
||||||
final OptionSet opts = parser.parse(args);
|
final OptionSpec<String> name = optparser.accepts("name", "The name of the wallet").withRequiredArg()
|
||||||
|
.ofType(String.class).defaultsTo("opretwallet");
|
||||||
|
|
||||||
|
optparser.accepts("help", "Displays program options");
|
||||||
|
final OptionSet opts = optparser.parse(args);
|
||||||
if (opts.has("help")) {
|
if (opts.has("help")) {
|
||||||
System.err.println("usage: App --net=MAIN/TEST/REGTEST");
|
System.err.println("usage: App [--net=MAIN/TEST/REGTEST] [--name=<name>]");
|
||||||
parser.printHelpOn(System.err);
|
optparser.printHelpOn(System.err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!opts.has(net)) {
|
if (!opts.has(net)) {
|
||||||
System.err.println("No net specified, using TestNet!");
|
System.err.println("No net specified, using TestNet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
final NetworkParameters params = net.value(opts).get();
|
WALLETNAME = name.value(opts);
|
||||||
|
PARAMS = net.value(opts).get();
|
||||||
|
|
||||||
final OPRETECParser bs = new OPRETECParser();
|
final boolean chk = PARSER.cryptoSelfTest();
|
||||||
|
|
||||||
final boolean chk = bs.cryptoSelfTest();
|
|
||||||
if (chk) {
|
if (chk) {
|
||||||
System.err.println("Crypto self test: PASSED");
|
System.err.println("Crypto self test: PASSED");
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,24 +300,47 @@ public class App {
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.addOPRETECRevokeEventListener((key) -> {
|
PARSER.addOPRETECRevokeEventListener((key) -> {
|
||||||
System.out.println("Revoked Key: " + Utils.HEX.encode(key.toBytes()));
|
System.out.println("Revoked Key: " + Utils.HEX.encode(key.toBytes()));
|
||||||
});
|
});
|
||||||
|
|
||||||
long earliestTime;
|
if (PARAMS.getId().equals(NetworkParameters.ID_REGTEST)) {
|
||||||
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
} else if (PARAMS.getId().equals(NetworkParameters.ID_TESTNET)) {
|
||||||
earliestTime = OPRET_BIRTHDAY;
|
|
||||||
} else if (params.getId().equals(NetworkParameters.ID_TESTNET)) {
|
|
||||||
earliestTime = OPRET_BIRTHDAY;
|
|
||||||
} else {
|
} else {
|
||||||
earliestTime = Utils.currentTimeSeconds();
|
Utils.currentTimeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.addVerifyKey(SK.getMasterVerifyKey(), earliestTime);
|
/*
|
||||||
|
* final MasterVerifyKey SVK = SK.getMasterVerifyKey();
|
||||||
|
* WATCHKEYS.add(SVK);
|
||||||
|
*
|
||||||
|
* for (final MasterVerifyKey m : WATCHKEYS) { PARSER.addVerifyKey(m,
|
||||||
|
* earliestTime); }
|
||||||
|
*
|
||||||
|
* scanBlockchain();
|
||||||
|
*/
|
||||||
|
|
||||||
final OPRETWalletAppKit kit = new OPRETWalletAppKit(params, new File("."), "opretwallet" + params.getId(), bs);
|
handleConsole();
|
||||||
|
if (KIT != null) {
|
||||||
|
System.out.println("shutting down");
|
||||||
|
KIT.stopAsync();
|
||||||
|
KIT.awaitTerminated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kit.addListener(new Service.Listener() {
|
public static void scanBlockchain() {
|
||||||
|
if (!PARSER.needScan()) {
|
||||||
|
System.err.println("No scan needed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (KIT != null) {
|
||||||
|
KIT.rescanBlockchain();
|
||||||
|
}
|
||||||
|
KIT = new OPRETWalletAppKit(PARAMS, new File("."), WALLETNAME + PARAMS.getId(), PARSER);
|
||||||
|
|
||||||
|
while (PARSER.needScan()) {
|
||||||
|
|
||||||
|
KIT.addListener(new Service.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void failed(final Service.State from, final Throwable failure) {
|
public void failed(final Service.State from, final Throwable failure) {
|
||||||
logger.error(failure.getMessage());
|
logger.error(failure.getMessage());
|
||||||
|
@ -209,30 +348,36 @@ public class App {
|
||||||
}
|
}
|
||||||
}, Threading.SAME_THREAD);
|
}, Threading.SAME_THREAD);
|
||||||
|
|
||||||
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
if (PARAMS.getId().equals(NetworkParameters.ID_REGTEST)) {
|
||||||
kit.connectToLocalHost();
|
KIT.connectToLocalHost();
|
||||||
}
|
}
|
||||||
final InputStream is = App.class.getResourceAsStream("/" + params.getId() + ".checkpoints");
|
final InputStream is = App.class.getResourceAsStream("/" + PARAMS.getId() + ".checkpoints");
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
kit.setCheckpoints(is);
|
KIT.setCheckpoints(is);
|
||||||
}
|
}
|
||||||
kit.startAsync();
|
KIT.startAsync();
|
||||||
|
|
||||||
System.out.println("Please wait for the blockchain to be downloaded!");
|
System.out.println("Please wait for the blockchain to be downloaded!");
|
||||||
|
|
||||||
|
PARSER.willScan();
|
||||||
try {
|
try {
|
||||||
kit.awaitRunning();
|
KIT.awaitRunning();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
System.err.println("Aborting - shutting down");
|
System.err.println("Aborting - shutting down");
|
||||||
// e.printStackTrace();
|
// e.printStackTrace();
|
||||||
kit.stopAsync();
|
KIT.stopAsync();
|
||||||
kit.awaitTerminated();
|
KIT.awaitTerminated();
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
// after gathering all the key, replay the blockchain
|
||||||
|
if (PARSER.needScan()) {
|
||||||
|
System.out.println("Rescanning the blockchain for the newly learned keys!");
|
||||||
|
KIT.rescanBlockchain();
|
||||||
|
KIT = new OPRETWalletAppKit(PARAMS, new File("."), WALLETNAME + PARAMS.getId(), PARSER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final OPRETWallet wallet = kit.opretwallet();
|
KIT.opretwallet().addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
||||||
|
|
||||||
wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
|
||||||
final Coin c = tx.getValue(wallet1);
|
final Coin c = tx.getValue(wallet1);
|
||||||
|
|
||||||
if (c.isPositive()) {
|
if (c.isPositive()) {
|
||||||
|
@ -267,21 +412,89 @@ public class App {
|
||||||
* tx.getConfidence(); System.out.println("new block depth: " +
|
* tx.getConfidence(); System.out.println("new block depth: " +
|
||||||
* confidence.getDepthInBlocks()); });
|
* confidence.getDepthInBlocks()); });
|
||||||
*/
|
*/
|
||||||
// wallet.allowSpendingUnconfirmedTransactions();
|
KIT.opretwallet().allowSpendingUnconfirmedTransactions();
|
||||||
|
|
||||||
handleConsole(kit);
|
|
||||||
|
|
||||||
System.out.println("shutting down");
|
|
||||||
kit.stopAsync();
|
|
||||||
kit.awaitTerminated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean sendOPReturn(final OPRETWalletAppKit kit, final PrintWriter output) {
|
private static boolean sendAnnounceFirst(MasterSigningKey key, MasterVerifyKey subkey, final OPRETWalletAppKit kit,
|
||||||
|
final PrintWriter output) {
|
||||||
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
|
Transaction t = new Transaction(params);
|
||||||
|
final Script[] scripts = OPRETECParser.getAnnounceFirstScript(key, subkey);
|
||||||
|
t.addOutput(Coin.ZERO, scripts[0]);
|
||||||
|
SendRequest request = SendRequest.forTx(t);
|
||||||
|
request.ensureMinRequiredFee = true;
|
||||||
|
request.shuffleOutputs = false;
|
||||||
|
try {
|
||||||
|
wallet.sendCoins(request);
|
||||||
|
} catch (final InsufficientMoneyException e) {
|
||||||
|
output.println(e.getLocalizedMessage());
|
||||||
|
output.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.debug("SendRequest {}", request);
|
||||||
|
|
||||||
|
t = new Transaction(params);
|
||||||
|
t.addOutput(Coin.ZERO, scripts[1]);
|
||||||
|
request = SendRequest.forTx(t);
|
||||||
|
request.ensureMinRequiredFee = true;
|
||||||
|
request.shuffleOutputs = false;
|
||||||
|
try {
|
||||||
|
wallet.sendCoins(request);
|
||||||
|
} catch (final InsufficientMoneyException e) {
|
||||||
|
output.println(e.getLocalizedMessage());
|
||||||
|
output.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("SendRequest {}", request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean sendAnnounceNext(MasterSigningKey key, MasterVerifyKey prev, MasterVerifyKey next,
|
||||||
|
final OPRETWalletAppKit kit, final PrintWriter output) {
|
||||||
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
|
Transaction t = new Transaction(params);
|
||||||
|
final Script[] scripts = OPRETECParser.getAnnounceNextScript(key, prev, next);
|
||||||
|
t.addOutput(Coin.ZERO, scripts[0]);
|
||||||
|
SendRequest request = SendRequest.forTx(t);
|
||||||
|
request.ensureMinRequiredFee = true;
|
||||||
|
request.shuffleOutputs = false;
|
||||||
|
try {
|
||||||
|
wallet.sendCoins(request);
|
||||||
|
} catch (final InsufficientMoneyException e) {
|
||||||
|
output.println(e.getLocalizedMessage());
|
||||||
|
output.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.debug("SendRequest {}", request);
|
||||||
|
|
||||||
|
t = new Transaction(params);
|
||||||
|
t.addOutput(Coin.ZERO, scripts[1]);
|
||||||
|
request = SendRequest.forTx(t);
|
||||||
|
request.ensureMinRequiredFee = true;
|
||||||
|
request.shuffleOutputs = false;
|
||||||
|
try {
|
||||||
|
wallet.sendCoins(request);
|
||||||
|
} catch (final InsufficientMoneyException e) {
|
||||||
|
output.println(e.getLocalizedMessage());
|
||||||
|
output.flush();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("SendRequest {}", request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean sendOPReturn(MasterSigningKey key, final OPRETWalletAppKit kit, final PrintWriter output) {
|
||||||
final OPRETWallet wallet = kit.opretwallet();
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
final NetworkParameters params = wallet.getNetworkParameters();
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
final Transaction t = new Transaction(params);
|
final Transaction t = new Transaction(params);
|
||||||
final Script script = OPRETECParser.getRevokeScript(SK);
|
final Script script = OPRETECParser.getRevokeScript(key);
|
||||||
t.addOutput(Coin.ZERO, script);
|
t.addOutput(Coin.ZERO, script);
|
||||||
final SendRequest request = SendRequest.forTx(t);
|
final SendRequest request = SendRequest.forTx(t);
|
||||||
request.ensureMinRequiredFee = true;
|
request.ensureMinRequiredFee = true;
|
||||||
|
|
|
@ -8,14 +8,17 @@
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<logger name="org.tcpid.opretj.testapp.App" level="debug" />
|
|
||||||
<logger name="org.bitcoinj.core.listeners.DownloadProgressTracker" level="info" />
|
<logger name="org.bitcoinj.core.listeners.DownloadProgressTracker" level="info" />
|
||||||
<logger name="org.bitcoinj.core.PeerGroup" level="error" />
|
<logger name="org.bitcoinj.core.PeerGroup" level="error" />
|
||||||
<logger name="org.bitcoinj.core.AbstractBlockChain" level="error" />
|
<logger name="org.bitcoinj.core.AbstractBlockChain" level="error" />
|
||||||
<logger name="org.bitcoinj.core.PeerSocketHandler" level="error" />
|
<logger name="org.bitcoinj.core.PeerSocketHandler" level="error" />
|
||||||
<logger name="org.bitcoinj.net.ConnectionHandler" level="error" />
|
<logger name="org.bitcoinj.net.ConnectionHandler" level="error" />
|
||||||
<logger name="org.tcpid.opretj.OPRETECParser" level="debug" />
|
<logger name="org.tcpid.opretj.testapp.App" level="debug" />
|
||||||
|
<logger name="org.tcpid.opret.OPRETECParser" level="info" />
|
||||||
|
<logger name="org.tcpid.opret.TestCrypto" level="debug" />
|
||||||
|
<logger name="org.tcpid.opret.TestPushTransaction" level="debug" />
|
||||||
<!--
|
<!--
|
||||||
|
<logger name="org.tcpid.opretj.OPRETWallet" level="debug" />
|
||||||
<logger name="eckey.OPRETSimpleLogger" level="debug" />
|
<logger name="eckey.OPRETSimpleLogger" level="debug" />
|
||||||
<logger name="eckey.OPRETSimpleParser" level="debug" />
|
<logger name="eckey.OPRETSimpleParser" level="debug" />
|
||||||
<logger name="eckey.OPRETBaseHandler" level="debug" />
|
<logger name="eckey.OPRETBaseHandler" level="debug" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opret;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -30,7 +30,7 @@ public class TestCrypto {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDerive() {
|
public void testDerive() {
|
||||||
|
logger.debug("testDerive");
|
||||||
assertTrue("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES", NONCE_BYTES <= HMACSHA512256.HMACSHA512256_BYTES);
|
assertTrue("NONCE_BYTES > HMACSHA512256.HMACSHA512256_BYTES", NONCE_BYTES <= HMACSHA512256.HMACSHA512256_BYTES);
|
||||||
assertEquals(SECRETKEY_BYTES, HMACSHA512256.HMACSHA512256_BYTES);
|
assertEquals(SECRETKEY_BYTES, HMACSHA512256.HMACSHA512256_BYTES);
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ public class TestCrypto {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSign() {
|
public void testSign() {
|
||||||
|
logger.debug("testSign");
|
||||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||||
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), vk.toHash());
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), vk.toHash());
|
||||||
|
@ -66,9 +67,11 @@ public class TestCrypto {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignEnc() {
|
public void testSignEnc() {
|
||||||
|
logger.debug("testSignEnc");
|
||||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
|
final MasterVerifyKey subkey = msk.getSubKey(1L).getMasterVerifyKey();
|
||||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||||
byte[] sig = msk.sign(vk.toBytes());
|
byte[] sig = msk.sign(subkey.toBytes());
|
||||||
|
|
||||||
logger.debug("using key {}", Encoder.HEX.encode(vk.toBytes()));
|
logger.debug("using key {}", Encoder.HEX.encode(vk.toBytes()));
|
||||||
final byte[] noncebytes = Util.zeros(32);
|
final byte[] noncebytes = Util.zeros(32);
|
||||||
|
@ -78,7 +81,7 @@ public class TestCrypto {
|
||||||
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
|
||||||
final byte[] cipher = Util.zeros(96);
|
final byte[] cipher = Util.zeros(96);
|
||||||
byte[] msg = Bytes.concat(vk.toBytes(), sig);
|
byte[] msg = Bytes.concat(subkey.toBytes(), sig);
|
||||||
assertEquals(96, msg.length);
|
assertEquals(96, msg.length);
|
||||||
|
|
||||||
sodium();
|
sodium();
|
||||||
|
@ -93,11 +96,49 @@ public class TestCrypto {
|
||||||
sig = Arrays.copyOfRange(msg, 32, 96);
|
sig = Arrays.copyOfRange(msg, 32, 96);
|
||||||
logger.debug("vkb : {}", Encoder.HEX.encode(vkb));
|
logger.debug("vkb : {}", Encoder.HEX.encode(vkb));
|
||||||
assertTrue("Verification of signature failed.", vk.verify(vkb, sig));
|
assertTrue("Verification of signature failed.", vk.verify(vkb, sig));
|
||||||
assertArrayEquals(vk.toBytes(), vkb);
|
assertArrayEquals(subkey.toBytes(), vkb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignEncNext() {
|
||||||
|
logger.debug("testSignEncNext");
|
||||||
|
|
||||||
|
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
|
final MasterVerifyKey mvk = msk.getMasterVerifyKey();
|
||||||
|
final MasterVerifyKey prev = msk.getSubKey(1L).getMasterVerifyKey();
|
||||||
|
final MasterVerifyKey next = msk.getSubKey(2L).getMasterVerifyKey();
|
||||||
|
|
||||||
|
byte[] sig = msk.sign(next.toBytes());
|
||||||
|
|
||||||
|
logger.debug("using key {}", Encoder.HEX.encode(prev.toBytes()));
|
||||||
|
final byte[] sharedkey = HASH.sha256(HASH.sha256(prev.toBytes()));
|
||||||
|
final byte[] xornonce = Arrays.copyOfRange(HASH.sha256(sharedkey), 0, 24);
|
||||||
|
logger.debug("xornonce {}", Encoder.HEX.encode(xornonce));
|
||||||
|
logger.debug("sharedkey {}", Encoder.HEX.encode(sharedkey));
|
||||||
|
|
||||||
|
final byte[] cipher = Util.zeros(96);
|
||||||
|
byte[] msg = Bytes.concat(next.toBytes(), sig);
|
||||||
|
assertEquals(96, msg.length);
|
||||||
|
|
||||||
|
sodium();
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(cipher, msg, 96, xornonce, sharedkey);
|
||||||
|
assertEquals(96, cipher.length);
|
||||||
|
logger.debug("Clear : {}", Encoder.HEX.encode(msg));
|
||||||
|
logger.debug("Cipher: {}", Encoder.HEX.encode(cipher));
|
||||||
|
msg = Util.zeros(96);
|
||||||
|
Sodium.crypto_stream_xsalsa20_xor(msg, cipher, 96, xornonce, sharedkey);
|
||||||
|
|
||||||
|
final byte[] vkb = Arrays.copyOfRange(msg, 0, 32);
|
||||||
|
sig = Arrays.copyOfRange(msg, 32, 96);
|
||||||
|
logger.debug("vkb : {}", Encoder.HEX.encode(vkb));
|
||||||
|
assertTrue("Verification of signature failed.", mvk.verify(vkb, sig));
|
||||||
|
assertArrayEquals(next.toBytes(), vkb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignEncNoncebytes() {
|
public void testSignEncNoncebytes() {
|
||||||
|
logger.debug("testSignEncNoncebytes");
|
||||||
|
|
||||||
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
final MasterSigningKey msk = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
final MasterVerifyKey vk = msk.getMasterVerifyKey();
|
||||||
byte[] sig = msk.sign(vk.toBytes());
|
byte[] sig = msk.sign(vk.toBytes());
|
|
@ -0,0 +1,287 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.tcpid.opret;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.tcpid.key.MasterVerifyKey;
|
||||||
|
import org.tcpid.opretj.OPRETTransaction;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
|
public class TestPushTransaction {
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
logger = LoggerFactory.getLogger(TestPushTransaction.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for
|
||||||
|
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPushTransactionECA12() {
|
||||||
|
logger.debug("testPushTransactionECA12");
|
||||||
|
|
||||||
|
final byte[] cipher = Encoder.HEX.decode(
|
||||||
|
"a15b671a9890a6bd0b6ed9a50193a15283001ccd72e106198b32242a906c300e263fc31dbfdaad66c40fc9796db3a464ab4313a06bbcd88fc1d503110016114c1da8bdf6e58a82be18d33c1baa96e1a9fe9c6f939b6838b30972be2de53f12d0");
|
||||||
|
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
||||||
|
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
||||||
|
|
||||||
|
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
||||||
|
final byte[] nullbyte = {};
|
||||||
|
|
||||||
|
List<List<Byte>> opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
// create t3 and t4 to test if the PARSER handles garbage
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t3 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t4 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
final OPRETECParser parser = new OPRETECParser();
|
||||||
|
|
||||||
|
parser.addVerifyKey(mvk, 0);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
MasterVerifyKey subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for
|
||||||
|
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPushTransactionECA12WithNonce() {
|
||||||
|
logger.debug("testPushTransactionECA12WithNonce");
|
||||||
|
final byte[] cipher = Encoder.HEX.decode(
|
||||||
|
"24f99184d03a6ffa5826bd9300a7fb1cff264600f335b3c6042f15cb4d3d9019fa2a9c905cdf6f6c80178def845f0340e6d2e55a7dee433a5af984760adc23e187734e5e4e76aa22f3acab172262633139b6dcd11229fe2385661a70d6c206c0");
|
||||||
|
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
||||||
|
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
||||||
|
|
||||||
|
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
||||||
|
final byte[] nullbyte = {};
|
||||||
|
|
||||||
|
List<List<Byte>> opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
||||||
|
final byte[] byte1f = { (byte) 0x11 };
|
||||||
|
opret_data.add(Bytes.asList(Bytes.concat(Arrays.copyOfRange(cipher, 0, 48), byte1f)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||||
|
opret_data.add(Bytes.asList(vkbsha96));
|
||||||
|
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
final OPRETECParser parser = new OPRETECParser();
|
||||||
|
|
||||||
|
parser.addVerifyKey(mvk, 0);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
MasterVerifyKey subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test method for
|
||||||
|
* {@link org.tcpid.opret.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testPushTransactionECA34() {
|
||||||
|
logger.debug("testPushTransactionECA34");
|
||||||
|
|
||||||
|
final byte[] cipher = Encoder.HEX.decode(
|
||||||
|
"6c44299f0b00638ca3e8a42d29082fb8f007eaff491472f9329c950b9181bfece5b7bf749b9b941dd92816c62b89f4673848ffe435576ee4e39d2e5f26041daff667c25dee1116a3f9f223de565465cafb2fdf3422c8c905666c268f7244850c");
|
||||||
|
final MasterVerifyKey mvk = new MasterVerifyKey(
|
||||||
|
Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c"));
|
||||||
|
final MasterVerifyKey firstsub = new MasterVerifyKey(
|
||||||
|
Encoder.HEX.decode("e4acb361f4ec55804af6b5a1bbf5ca74ad78b4edc9a977a1dfed08872aa0a5db"));
|
||||||
|
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
|
||||||
|
|
||||||
|
final byte[] nullbyte = {};
|
||||||
|
|
||||||
|
List<List<Byte>> opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca3")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||||
|
opret_data.add(Bytes.asList(mvk.getShortHash()));
|
||||||
|
opret_data.add(Bytes.asList(firstsub.getShortHash()));
|
||||||
|
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca4")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||||
|
opret_data.add(Bytes.asList(mvk.getShortHash()));
|
||||||
|
opret_data.add(Bytes.asList(firstsub.getShortHash()));
|
||||||
|
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
// create t3 and t4 to test if the PARSER handles garbage
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca4")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
||||||
|
opret_data.add(Bytes.asList(mvk.getShortHash()));
|
||||||
|
opret_data.add(Bytes.asList(firstsub.getShortHash()));
|
||||||
|
final OPRETTransaction t3 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
opret_data = new ArrayList<>();
|
||||||
|
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca3")));
|
||||||
|
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
||||||
|
opret_data.add(Bytes.asList(mvk.getShortHash()));
|
||||||
|
opret_data.add(Bytes.asList(firstsub.getShortHash()));
|
||||||
|
final OPRETTransaction t4 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
||||||
|
|
||||||
|
final OPRETECParser parser = new OPRETECParser();
|
||||||
|
|
||||||
|
parser.addVerifyKey(mvk, 0);
|
||||||
|
parser.addVerifyKey(firstsub, 0);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
|
||||||
|
firstsub.setRevoked(true);
|
||||||
|
MasterVerifyKey subkey = mvk.getValidSubKey();
|
||||||
|
logger.debug("FirstValid: {}", subkey.toString());
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
|
||||||
|
firstsub.setRevoked(true);
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t1));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertTrue(parser.pushTransaction(t2));
|
||||||
|
|
||||||
|
firstsub.setRevoked(true);
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
|
||||||
|
|
||||||
|
mvk.clearSubKeys();
|
||||||
|
mvk.setFirstValidSubKey(firstsub, (OPRETTransaction) null, (OPRETTransaction) null);
|
||||||
|
|
||||||
|
assertFalse(parser.pushTransaction(t2));
|
||||||
|
assertFalse(parser.pushTransaction(t4));
|
||||||
|
assertFalse(parser.pushTransaction(t3));
|
||||||
|
assertTrue(parser.pushTransaction(t1));
|
||||||
|
|
||||||
|
firstsub.setRevoked(true);
|
||||||
|
subkey = mvk.getValidSubKey();
|
||||||
|
assertArrayEquals(subkey.toBytes(),
|
||||||
|
Encoder.HEX.decode("11e14458b16050a23a772e469ee424f513c3eb81682c0f9f81f07e607c6bf917"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
eclipse.preferences.version=1
|
eclipse.preferences.version=1
|
||||||
encoding//src/main/java=UTF-8
|
encoding//src/main/java=UTF-8
|
||||||
encoding//src/test/java=UTF-8
|
|
||||||
encoding/<project>=UTF-8
|
encoding/<project>=UTF-8
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
package org.tcpid.key;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
import org.tcpid.opretj.OPRETTransaction;
|
|
||||||
|
|
||||||
public class MasterVerifyKey extends VerifyKey {
|
|
||||||
private final LinkedList<VerifyKey> subkeys = new LinkedList<>();
|
|
||||||
|
|
||||||
public MasterVerifyKey(final byte[] key) {
|
|
||||||
super(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSubKeys() {
|
|
||||||
subkeys.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VerifyKey getValidSubKey() {
|
|
||||||
return subkeys.getFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void revokeSubKey(final VerifyKey key) {
|
|
||||||
final int i = subkeys.indexOf(key);
|
|
||||||
|
|
||||||
if (i == -1) {
|
|
||||||
throw new NoSuchElementException("No such subkey");
|
|
||||||
}
|
|
||||||
|
|
||||||
subkeys.get(i).setRevoked(true);
|
|
||||||
subkeys.remove(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFirstValidSubKey(final VerifyKey key, final OPRETTransaction t1, final OPRETTransaction t2) {
|
|
||||||
if (!subkeys.isEmpty()) {
|
|
||||||
throw new IndexOutOfBoundsException("Subkey list is not empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
subkeys.addLast(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNextValidSubKey(final VerifyKey after, final VerifyKey key) {
|
|
||||||
final VerifyKey l = subkeys.getLast();
|
|
||||||
if (!l.equals(after)) {
|
|
||||||
throw new NoSuchElementException("No such after key, or not last in list");
|
|
||||||
}
|
|
||||||
|
|
||||||
subkeys.addLast(key);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,8 +6,6 @@ import java.util.concurrent.Executor;
|
||||||
|
|
||||||
public interface OPRETHandlerInterface {
|
public interface OPRETHandlerInterface {
|
||||||
|
|
||||||
// void addOPRET(byte[] magic, long earliestTime);
|
|
||||||
|
|
||||||
void addOPRETChangeEventListener(Executor executor, OPRETChangeEventListener listener);
|
void addOPRETChangeEventListener(Executor executor, OPRETChangeEventListener listener);
|
||||||
|
|
||||||
long getEarliestElementCreationTime();
|
long getEarliestElementCreationTime();
|
||||||
|
@ -16,10 +14,8 @@ public interface OPRETHandlerInterface {
|
||||||
|
|
||||||
Set<List<Byte>> getOPRETSet();
|
Set<List<Byte>> getOPRETSet();
|
||||||
|
|
||||||
// void removeOPRET(byte[] magic);
|
|
||||||
|
|
||||||
void pushTransaction(OPRETTransaction t);
|
|
||||||
|
|
||||||
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
||||||
|
|
||||||
|
void pushTransactions(List<OPRETTransaction> pushlist);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,7 @@ import com.google.common.primitives.Bytes;
|
||||||
public class OPRETSimpleLogger extends OPRETBaseHandler {
|
public class OPRETSimpleLogger extends OPRETBaseHandler {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OPRETSimpleLogger.class);
|
private static final Logger logger = LoggerFactory.getLogger(OPRETSimpleLogger.class);
|
||||||
|
|
||||||
@Override
|
public boolean pushTransaction(final OPRETTransaction t) {
|
||||||
public void pushTransaction(final OPRETTransaction t) {
|
|
||||||
final StringBuilder buf = new StringBuilder();
|
final StringBuilder buf = new StringBuilder();
|
||||||
|
|
||||||
for (final List<Byte> d : t.opretData) {
|
for (final List<Byte> d : t.opretData) {
|
||||||
|
@ -21,6 +20,14 @@ public class OPRETSimpleLogger extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Received in Block: {}\nTX: {}\nData: {}", t.blockHash, t.txHash, buf);
|
logger.info("Received in Block: {}\nTX: {}\nData: {}", t.blockHash, t.txHash, buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushTransactions(List<OPRETTransaction> pushlist) {
|
||||||
|
for (final OPRETTransaction t : pushlist) {
|
||||||
|
pushTransaction(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opretj;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
import org.bitcoinj.core.PartialMerkleTree;
|
||||||
|
@ -18,11 +19,13 @@ public class OPRETTransaction implements Serializable {
|
||||||
public final Sha256Hash txHash;
|
public final Sha256Hash txHash;
|
||||||
public final List<List<Byte>> opretData;
|
public final List<List<Byte>> opretData;
|
||||||
private PartialMerkleTree partialMerkleTree;
|
private PartialMerkleTree partialMerkleTree;
|
||||||
|
private Date date;
|
||||||
|
|
||||||
public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final List<List<Byte>> opret_data) {
|
public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final List<List<Byte>> opret_data) {
|
||||||
this.blockHash = blockHash;
|
this.blockHash = blockHash;
|
||||||
this.txHash = txHash;
|
this.txHash = txHash;
|
||||||
this.opretData = opret_data;
|
this.opretData = opret_data;
|
||||||
|
this.date = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PartialMerkleTree getPartialMerkleTree() {
|
public PartialMerkleTree getPartialMerkleTree() {
|
||||||
|
@ -45,4 +48,14 @@ public class OPRETTransaction implements Serializable {
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTime(Date time) {
|
||||||
|
if (this.date == null) {
|
||||||
|
this.date = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTime() {
|
||||||
|
return this.date;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,6 +142,7 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
@Override
|
@Override
|
||||||
public void onBlocksDownloaded(final Peer peer, final Block block, final FilteredBlock filteredBlock,
|
public void onBlocksDownloaded(final Peer peer, final Block block, final FilteredBlock filteredBlock,
|
||||||
final int blocksLeft) {
|
final int blocksLeft) {
|
||||||
|
final ArrayList<OPRETTransaction> pushlist = new ArrayList<>();
|
||||||
|
|
||||||
if (!pendingTransactions.containsKey(block.getHash())) {
|
if (!pendingTransactions.containsKey(block.getHash())) {
|
||||||
return;
|
return;
|
||||||
|
@ -149,7 +150,12 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
|
|
||||||
for (final OPRETTransaction t : pendingTransactions.get(block.getHash()).values()) {
|
for (final OPRETTransaction t : pendingTransactions.get(block.getHash()).values()) {
|
||||||
t.setPartialMerkleTree(filteredBlock.getPartialMerkleTree());
|
t.setPartialMerkleTree(filteredBlock.getPartialMerkleTree());
|
||||||
opbs.pushTransaction(t);
|
t.setTime(block.getTime());
|
||||||
|
pushlist.add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pushlist.isEmpty()) {
|
||||||
|
opbs.pushTransactions(pushlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingTransactions.remove(block.getHash());
|
pendingTransactions.remove(block.getHash());
|
||||||
|
@ -172,6 +178,7 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
logger.debug("False Positive Transaction {}", tx.toString());
|
logger.debug("False Positive Transaction {}", tx.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
logger.debug("Found Transaction {}", tx.toString());
|
||||||
final Sha256Hash h = block.getHeader().getHash();
|
final Sha256Hash h = block.getHeader().getHash();
|
||||||
|
|
||||||
if (!pendingTransactions.containsKey(h)) {
|
if (!pendingTransactions.containsKey(h)) {
|
||||||
|
|
|
@ -15,12 +15,14 @@ import org.bitcoinj.wallet.Wallet;
|
||||||
public class OPRETWalletAppKit extends WalletAppKit {
|
public class OPRETWalletAppKit extends WalletAppKit {
|
||||||
// private final Logger logger = LoggerFactory.getLogger(OPRETWallet.class);
|
// private final Logger logger = LoggerFactory.getLogger(OPRETWallet.class);
|
||||||
private final OPRETHandlerInterface opbs;
|
private final OPRETHandlerInterface opbs;
|
||||||
|
private File spvblockstorefile;
|
||||||
|
|
||||||
public OPRETWalletAppKit(final NetworkParameters params, final File directory, final String filePrefix,
|
public OPRETWalletAppKit(final NetworkParameters params, final File directory, final String filePrefix,
|
||||||
final OPRETHandlerInterface bs) {
|
final OPRETHandlerInterface bs) {
|
||||||
super(params, directory, filePrefix);
|
super(params, directory, filePrefix);
|
||||||
opbs = bs;
|
opbs = bs;
|
||||||
walletFactory = (params1, keyChainGroup) -> new OPRETWallet(params1, keyChainGroup, opbs);
|
walletFactory = (params1, keyChainGroup) -> new OPRETWallet(params1, keyChainGroup, opbs);
|
||||||
|
spvblockstorefile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,6 +55,21 @@ public class OPRETWalletAppKit extends WalletAppKit {
|
||||||
|| params.getId().equals(NetworkParameters.ID_TESTNET)) {
|
|| params.getId().equals(NetworkParameters.ID_TESTNET)) {
|
||||||
file.deleteOnExit();
|
file.deleteOnExit();
|
||||||
}
|
}
|
||||||
|
this.spvblockstorefile = file;
|
||||||
return new SPVBlockStore(params, file);
|
return new SPVBlockStore(params, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void rescanBlockchain() {
|
||||||
|
wallet().clearTransactions(0);
|
||||||
|
wallet().setLastBlockSeenHeight(-1); // magic value
|
||||||
|
wallet().setLastBlockSeenHash(null);
|
||||||
|
|
||||||
|
stopAsync();
|
||||||
|
awaitTerminated();
|
||||||
|
try {
|
||||||
|
this.spvblockstorefile.delete();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.tcpid.opretj;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.libsodium.jni.encoders.Encoder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.tcpid.key.MasterVerifyKey;
|
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
|
|
||||||
public class TestECA1 {
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(TestECA1.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test method for
|
|
||||||
* {@link org.tcpid.opretj.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testPushTransaction() {
|
|
||||||
logger.debug("testPushTransaction");
|
|
||||||
|
|
||||||
final byte[] cipher = Encoder.HEX.decode(
|
|
||||||
"bed9e277c3fde807eecb2100e2a4c9ec1067891b9f021e3bfbc599a3676048598e7c9801d94d9765cb965e64cfb9f493d7ae332bc85affb8bb0337b6835c51d156005db43ab8ea9b988632bfadcaee7dabf08709be248f5354d59a98e53f0cda");
|
|
||||||
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
|
||||||
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
|
||||||
|
|
||||||
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
|
||||||
final byte[] nullbyte = {};
|
|
||||||
|
|
||||||
List<List<Byte>> opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
|
||||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
|
||||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
|
||||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 0, 48)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t3 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
|
||||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t4 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
final OPRETECParser parser = new OPRETECParser();
|
|
||||||
|
|
||||||
parser.addVerifyKey(mvk, 0);
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t2));
|
|
||||||
assertFalse(parser.handleTransaction(t3));
|
|
||||||
assertFalse(parser.handleTransaction(t4));
|
|
||||||
assertTrue(parser.handleTransaction(t1));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t1));
|
|
||||||
assertFalse(parser.handleTransaction(t3));
|
|
||||||
assertFalse(parser.handleTransaction(t4));
|
|
||||||
assertTrue(parser.handleTransaction(t2));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t1));
|
|
||||||
assertFalse(parser.handleTransaction(t4));
|
|
||||||
assertFalse(parser.handleTransaction(t3));
|
|
||||||
assertTrue(parser.handleTransaction(t2));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t2));
|
|
||||||
assertFalse(parser.handleTransaction(t4));
|
|
||||||
assertFalse(parser.handleTransaction(t3));
|
|
||||||
assertTrue(parser.handleTransaction(t1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test method for
|
|
||||||
* {@link org.tcpid.opretj.OPRETECParser#pushTransaction(org.tcpid.opretj.OPRETTransaction)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testPushTransactionWithNonce() {
|
|
||||||
logger.debug("testPushTransactionWithNonce");
|
|
||||||
final byte[] cipher = Encoder.HEX.decode(
|
|
||||||
"24f99184d03a6ffa5826bd9300a7fb1cff264600f335b3c6042f15cb4d3d9019fa2a9c905cdf6f6c80178def845f0340e6d2e55a7dee433a5af984760adc23e187734e5e4e76aa22f3acab172262633139b6dcd11229fe2385661a70d6c206c0");
|
|
||||||
final byte[] vkb = Encoder.HEX.decode("fb2e360caf811b3aaf534d0458c2a2ca3e1f213b244a6f83af1ab50eddacdd8c");
|
|
||||||
final MasterVerifyKey mvk = new MasterVerifyKey(vkb);
|
|
||||||
|
|
||||||
final byte[] vkbsha96 = Arrays.copyOfRange(Sha256Hash.of(vkb).getBytes(), 0, 12);
|
|
||||||
final byte[] nullbyte = {};
|
|
||||||
|
|
||||||
List<List<Byte>> opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca1")));
|
|
||||||
final byte[] byte1f = { (byte) 0x11 };
|
|
||||||
opret_data.add(Bytes.asList(Bytes.concat(Arrays.copyOfRange(cipher, 0, 48), byte1f)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t1 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
opret_data = new ArrayList<>();
|
|
||||||
opret_data.add(Bytes.asList(Encoder.HEX.decode("eca2")));
|
|
||||||
opret_data.add(Bytes.asList(Arrays.copyOfRange(cipher, 48, 96)));
|
|
||||||
opret_data.add(Bytes.asList(vkbsha96));
|
|
||||||
final OPRETTransaction t2 = new OPRETTransaction(Sha256Hash.of(nullbyte), Sha256Hash.of(nullbyte), opret_data);
|
|
||||||
|
|
||||||
final OPRETECParser parser = new OPRETECParser();
|
|
||||||
|
|
||||||
parser.addVerifyKey(mvk, 0);
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t2));
|
|
||||||
assertTrue(parser.handleTransaction(t1));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t1));
|
|
||||||
assertTrue(parser.handleTransaction(t2));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t1));
|
|
||||||
assertTrue(parser.handleTransaction(t2));
|
|
||||||
|
|
||||||
mvk.clearSubKeys();
|
|
||||||
|
|
||||||
assertFalse(parser.handleTransaction(t2));
|
|
||||||
assertTrue(parser.handleTransaction(t1));
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue