begin derived keys
This commit is contained in:
parent
9842bc68ef
commit
35dd21f3c1
|
@ -21,7 +21,7 @@ import org.bitcoinj.wallet.SendRequest;
|
||||||
import org.libsodium.jni.crypto.Hash;
|
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.SigningKey;
|
import org.tcpid.key.MasterSigningKey;
|
||||||
import org.tcpid.opretj.OPRETECParser;
|
import org.tcpid.opretj.OPRETECParser;
|
||||||
import org.tcpid.opretj.OPRETWallet;
|
import org.tcpid.opretj.OPRETWallet;
|
||||||
import org.tcpid.opretj.OPRETWalletAppKit;
|
import org.tcpid.opretj.OPRETWalletAppKit;
|
||||||
|
@ -39,7 +39,7 @@ public class App {
|
||||||
public final static long OPRET_BIRTHDAY = 1471989600;
|
public final static long OPRET_BIRTHDAY = 1471989600;
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
||||||
private final static SigningKey SK = new SigningKey(HASH.sha256("TESTSEED".getBytes()));
|
private final static MasterSigningKey SK = new MasterSigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
|
|
||||||
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
|
private static void displayBalance(final OPRETWalletAppKit kit, final PrintWriter out) {
|
||||||
out.write("Balance: " + kit.wallet().getBalance().toFriendlyString() + "\n");
|
out.write("Balance: " + kit.wallet().getBalance().toFriendlyString() + "\n");
|
||||||
|
@ -157,7 +157,15 @@ public class App {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String[] args) throws Exception {
|
public static void main(final String[] args) throws Exception {
|
||||||
|
MasterSigningKey sk = SK.getSubKey(1L);
|
||||||
|
|
||||||
|
System.err.println(SK.toString());
|
||||||
|
System.err.println(sk.toString());
|
||||||
|
sk = SK.getSubKey(2L);
|
||||||
|
System.err.println(sk.toString());
|
||||||
|
sk = SK.getSubKey(3L);
|
||||||
|
System.err.println(sk.toString());
|
||||||
|
System.exit(0);
|
||||||
final OptionParser parser = new OptionParser();
|
final OptionParser parser = new OptionParser();
|
||||||
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
final OptionSpec<NetworkEnum> net = parser.accepts("net", "The network to run the examples on")
|
||||||
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
.withRequiredArg().ofType(NetworkEnum.class).defaultsTo(NetworkEnum.TEST);
|
||||||
|
|
71
opretj/src/main/java/org/tcpid/key/MasterSigningKey.java
Normal file
71
opretj/src/main/java/org/tcpid/key/MasterSigningKey.java
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package org.tcpid.key;
|
||||||
|
|
||||||
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.libsodium.jni.Sodium;
|
||||||
|
import org.libsodium.jni.crypto.Hash;
|
||||||
|
import org.libsodium.jni.crypto.Util;
|
||||||
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
|
public class MasterSigningKey extends SigningKey {
|
||||||
|
private MasterSigningKey subkey;
|
||||||
|
private Long subkeyindex;
|
||||||
|
public static final Hash HASH = new Hash();
|
||||||
|
private final ArrayList<Long> keyindex;
|
||||||
|
|
||||||
|
public MasterSigningKey(final byte[] key, ArrayList<Long> keyindex) {
|
||||||
|
super(key);
|
||||||
|
subkey = null;
|
||||||
|
subkeyindex = 0L;
|
||||||
|
this.keyindex = new ArrayList<>(keyindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterSigningKey(final byte[] key) {
|
||||||
|
super(key);
|
||||||
|
subkey = null;
|
||||||
|
subkeyindex = 0L;
|
||||||
|
this.keyindex = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterSigningKey getValidSubKey() {
|
||||||
|
if (subkey == null) {
|
||||||
|
subkey = getSubKey(subkeyindex);
|
||||||
|
}
|
||||||
|
return subkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterSigningKey getSubKey(Long offset) {
|
||||||
|
System.err.println("getSubKey");
|
||||||
|
final byte[] la = Longs.toByteArray(offset);
|
||||||
|
final byte[] keynum = Util.prependZeros(16 - la.length, la);
|
||||||
|
System.err.println("getSubKey key=" + Encoder.HEX.encode(keynum));
|
||||||
|
final byte[] appid = Util.prependZeros(14, "EC".getBytes());
|
||||||
|
final byte[] newseed = Util.zeros(SECRETKEY_BYTES);
|
||||||
|
sodium();
|
||||||
|
Sodium.crypto_generichash_blake2b_salt_personal(newseed, newseed.length, null, 0, this.seed, this.seed.length,
|
||||||
|
keynum, appid);
|
||||||
|
final ArrayList<Long> ki = new ArrayList<>(this.keyindex);
|
||||||
|
ki.add(offset);
|
||||||
|
return new MasterSigningKey(newseed, ki);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterSigningKey getNextValidSubKey(Long offset) {
|
||||||
|
return getSubKey(subkeyindex + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revokeSubKey() {
|
||||||
|
subkey.setRevoked(true);
|
||||||
|
subkeyindex++;
|
||||||
|
subkey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Index: " + this.keyindex.toString() + " " + Encoder.HEX.encode(seed);
|
||||||
|
}
|
||||||
|
}
|
48
opretj/src/main/java/org/tcpid/key/MasterVerifyKey.java
Normal file
48
opretj/src/main/java/org/tcpid/key/MasterVerifyKey.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package org.tcpid.key;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public class MasterVerifyKey extends VerifyKey {
|
||||||
|
private final LinkedList<VerifyKey> subkeys = new LinkedList<>();
|
||||||
|
|
||||||
|
public MasterVerifyKey(final byte[] key) {
|
||||||
|
super(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSubKeys() {
|
||||||
|
subkeys.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,48 +21,68 @@ import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||||
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||||
|
|
||||||
|
import org.libsodium.jni.Sodium;
|
||||||
import org.libsodium.jni.crypto.Random;
|
import org.libsodium.jni.crypto.Random;
|
||||||
import org.libsodium.jni.crypto.Util;
|
import org.libsodium.jni.crypto.Util;
|
||||||
import org.libsodium.jni.encoders.Encoder;
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
import org.libsodium.jni.keys.KeyPair;
|
import org.libsodium.jni.keys.KeyPair;
|
||||||
import org.libsodium.jni.keys.PrivateKey;
|
import org.libsodium.jni.keys.PrivateKey;
|
||||||
|
|
||||||
public class SigningKey {
|
public class SigningKey implements Comparable<SigningKey> {
|
||||||
|
|
||||||
|
protected final byte[] seed;
|
||||||
|
|
||||||
private final byte[] seed;
|
|
||||||
private final byte[] secretKey;
|
private final byte[] secretKey;
|
||||||
|
|
||||||
private final VerifyKey verifyKey;
|
private final VerifyKey verifyKey;
|
||||||
|
private boolean revoked;
|
||||||
|
|
||||||
public SigningKey() {
|
public SigningKey() {
|
||||||
this(new Random().randomBytes(SECRETKEY_BYTES));
|
this(new Random().randomBytes(SECRETKEY_BYTES));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SigningKey(byte[] seed) {
|
public SigningKey(final byte[] seed) {
|
||||||
Util.checkLength(seed, SECRETKEY_BYTES);
|
Util.checkLength(seed, SECRETKEY_BYTES);
|
||||||
this.seed = seed;
|
this.seed = seed.clone();
|
||||||
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
|
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
|
||||||
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
|
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
|
||||||
Util.isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
|
sodium();
|
||||||
|
Util.isValid(Sodium.crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
|
||||||
"Failed to generate a key pair");
|
"Failed to generate a key pair");
|
||||||
|
|
||||||
this.verifyKey = new VerifyKey(publicKey);
|
this.verifyKey = new VerifyKey(publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SigningKey(String seed, Encoder encoder) {
|
public SigningKey(final String seed, final Encoder encoder) {
|
||||||
this(encoder.decode(seed));
|
this(encoder.decode(seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final SigningKey other) {
|
||||||
|
for (int i = SECRETKEY_BYTES - 1; i >= 0; i--) {
|
||||||
|
final int thisByte = this.seed[i] & 0xff;
|
||||||
|
final int otherByte = other.seed[i] & 0xff;
|
||||||
|
if (thisByte > otherByte) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (thisByte < otherByte) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public KeyPair getKeyPair() {
|
public KeyPair getKeyPair() {
|
||||||
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||||
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
sodium();
|
||||||
|
Sodium.crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||||
return new KeyPair(sk);
|
return new KeyPair(sk);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PrivateKey getPrivateKey() {
|
public PrivateKey getPrivateKey() {
|
||||||
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||||
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
sodium();
|
||||||
|
Sodium.crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||||
return new PrivateKey(sk);
|
return new PrivateKey(sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,15 +90,26 @@ public class SigningKey {
|
||||||
return this.verifyKey;
|
return this.verifyKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] sign(byte[] message) {
|
public boolean isRevoked() {
|
||||||
|
return revoked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRevoked(final boolean revoked) {
|
||||||
|
if (this.revoked != true) {
|
||||||
|
this.revoked = revoked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] sign(final byte[] message) {
|
||||||
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
|
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
|
||||||
final int[] bufferLen = new int[1];
|
final int[] bufferLen = new int[1];
|
||||||
sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
|
sodium();
|
||||||
|
Sodium.crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
|
||||||
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
|
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String sign(String message, Encoder encoder) {
|
public String sign(final String message, final Encoder encoder) {
|
||||||
final byte[] signature = sign(encoder.decode(message));
|
final byte[] signature = sign(encoder.decode(message));
|
||||||
return encoder.encode(signature);
|
return encoder.encode(signature);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.libsodium.jni.Sodium;
|
||||||
import org.libsodium.jni.crypto.Hash;
|
import org.libsodium.jni.crypto.Hash;
|
||||||
import org.libsodium.jni.crypto.Util;
|
import org.libsodium.jni.crypto.Util;
|
||||||
import org.libsodium.jni.encoders.Encoder;
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
@ -34,18 +35,17 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
private final byte[] hash;
|
private final byte[] hash;
|
||||||
private final byte[] shortHash;
|
private final byte[] shortHash;
|
||||||
private boolean revoked;
|
private boolean revoked;
|
||||||
|
private MasterVerifyKey masterkey;
|
||||||
|
|
||||||
public boolean isRevoked() {
|
public MasterVerifyKey getMasterkey() {
|
||||||
return revoked;
|
return masterkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRevoked(boolean revoked) {
|
public void setMasterkey(MasterVerifyKey masterkey) {
|
||||||
if (this.revoked != true) {
|
this.masterkey = masterkey;
|
||||||
this.revoked = revoked;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VerifyKey(byte[] key) {
|
public VerifyKey(final byte[] key) {
|
||||||
Util.checkLength(key, PUBLICKEY_BYTES);
|
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.hash = HASH.sha256(key);
|
this.hash = HASH.sha256(key);
|
||||||
|
@ -53,58 +53,10 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
this.revoked = false;
|
this.revoked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VerifyKey(String key, Encoder encoder) {
|
public VerifyKey(final String key, final Encoder encoder) {
|
||||||
this(encoder.decode(key));
|
this(encoder.decode(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getPublicKey() {
|
|
||||||
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
|
||||||
sodium().crypto_sign_ed25519_pk_to_curve25519(pk, this.key);
|
|
||||||
return new PublicKey(pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toBytes() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Encoder.HEX.encode(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ((o == null) || (getClass() != o.getClass())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Arrays.equals(key, ((VerifyKey) o).key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(byte[] message, byte[] signature) {
|
|
||||||
Util.checkLength(signature, SIGNATURE_BYTES);
|
|
||||||
final byte[] sigAndMsg = Util.merge(signature, message);
|
|
||||||
final byte[] buffer = Util.zeros(sigAndMsg.length);
|
|
||||||
final int[] bufferLen = new int[1];
|
|
||||||
|
|
||||||
return Util.isValid(sodium().crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
|
||||||
"signature was forged or corrupted");
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String message, String signature, Encoder encoder) {
|
|
||||||
return verify(encoder.decode(message), encoder.decode(signature));
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toHash() {
|
|
||||||
return this.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getShortHash() {
|
|
||||||
return this.shortHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(final VerifyKey other) {
|
public int compareTo(final VerifyKey other) {
|
||||||
for (int i = PUBLICKEY_BYTES - 1; i >= 0; i--) {
|
for (int i = PUBLICKEY_BYTES - 1; i >= 0; i--) {
|
||||||
|
@ -120,4 +72,64 @@ public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((o == null) || (getClass() != o.getClass())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Arrays.equals(key, ((VerifyKey) o).key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublicKey getPublicKey() {
|
||||||
|
final byte[] pk = Util.zeros(PUBLICKEY_BYTES);
|
||||||
|
sodium();
|
||||||
|
Sodium.crypto_sign_ed25519_pk_to_curve25519(pk, this.key);
|
||||||
|
return new PublicKey(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getShortHash() {
|
||||||
|
return this.shortHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRevoked() {
|
||||||
|
return revoked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRevoked(final boolean revoked) {
|
||||||
|
if (this.revoked != true) {
|
||||||
|
this.revoked = revoked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toHash() {
|
||||||
|
return this.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Encoder.HEX.encode(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(final byte[] message, final byte[] signature) {
|
||||||
|
Util.checkLength(signature, SIGNATURE_BYTES);
|
||||||
|
final byte[] sigAndMsg = Util.merge(signature, message);
|
||||||
|
final byte[] buffer = Util.zeros(sigAndMsg.length);
|
||||||
|
final int[] bufferLen = new int[1];
|
||||||
|
|
||||||
|
sodium();
|
||||||
|
return Util.isValid(Sodium.crypto_sign_ed25519_open(buffer, bufferLen, sigAndMsg, sigAndMsg.length, key),
|
||||||
|
"signature was forged or corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(final String message, final String signature, final Encoder encoder) {
|
||||||
|
return verify(encoder.decode(message), encoder.decode(signature));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opretj;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -17,7 +18,7 @@ import com.google.common.primitives.Bytes;
|
||||||
public abstract class OPRETBaseHandler implements OPRETHandlerInterface {
|
public abstract class OPRETBaseHandler implements OPRETHandlerInterface {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OPRETBaseHandler.class);
|
private static final Logger logger = LoggerFactory.getLogger(OPRETBaseHandler.class);
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETChangeEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<ListenerRegistration<OPRETChangeEventListener>>();
|
private final CopyOnWriteArrayList<ListenerRegistration<OPRETChangeEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<ListenerRegistration<OPRETChangeEventListener>>();
|
||||||
private final Map<List<Byte>, Long> magicBytes = new HashMap<>();
|
private final Map<List<Byte>, Long> magicBytes = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
protected void addOPRET(final byte[] magic, final long earliestTime) {
|
protected void addOPRET(final byte[] magic, final long earliestTime) {
|
||||||
logger.debug("addMagicBytes: {} - Time {}", Utils.HEX.encode(magic), earliestTime);
|
logger.debug("addMagicBytes: {} - Time {}", Utils.HEX.encode(magic), earliestTime);
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
package org.tcpid.opretj;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
import org.bitcoinj.utils.ListenerRegistration;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
|
|
||||||
public class OPRETECExampleParser extends OPRETBaseHandler {
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OPRETECExampleParser.class);
|
|
||||||
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec1d"));
|
|
||||||
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = new HashMap<>();
|
|
||||||
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>();
|
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an event listener object. Methods on this object are called when
|
|
||||||
* scripts watched by this wallet change. The listener is executed by the
|
|
||||||
* given executor.
|
|
||||||
*/
|
|
||||||
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
|
||||||
// This is thread safe, so we don't need to take the lock.
|
|
||||||
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkData(final OPRETTransaction t1, final OPRETTransaction t2) {
|
|
||||||
final List<List<Byte>> opret_data = new ArrayList<>(t1.opretData);
|
|
||||||
opret_data.addAll(t2.opretData);
|
|
||||||
logger.debug("checking {}", opret_data);
|
|
||||||
|
|
||||||
List<Byte> chunk;
|
|
||||||
chunk = opret_data.get(0);
|
|
||||||
if (!chunk.equals(OPRET_MAGIC)) {
|
|
||||||
logger.debug("0: != OPRET_MAGIC");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk = opret_data.get(1);
|
|
||||||
if (chunk.size() != 1) {
|
|
||||||
logger.debug("1: size != 1");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunk.get(0) == (byte) 0xFE) {
|
|
||||||
if (opret_data.size() != 8) {
|
|
||||||
logger.debug("FE: size != 8");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
chunk = opret_data.get(4);
|
|
||||||
if (!chunk.equals(OPRET_MAGIC)) {
|
|
||||||
logger.debug("FE 4 != OPRET_MAGIC");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!opret_data.get(2).equals(opret_data.get(6))) {
|
|
||||||
logger.debug("FE 2 != 6");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
chunk = opret_data.get(5);
|
|
||||||
if ((chunk.size() != 1) || (chunk.get(0) != (byte) 0xFF)) {
|
|
||||||
logger.debug("FE 5 size!=1 or != FF");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return handleRevoke(t1, t2);
|
|
||||||
} else {
|
|
||||||
logger.debug("1: != 0xFE");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean handleRevoke(final OPRETTransaction t1, final OPRETTransaction t2) {
|
|
||||||
final byte[] pkhash = Bytes.toArray(t1.opretData.get(2));
|
|
||||||
final byte[] sig = Bytes.concat(Bytes.toArray(t1.opretData.get(3)), Bytes.toArray(t2.opretData.get(3)));
|
|
||||||
|
|
||||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
|
||||||
queueOnOPRETRevoke(pkhash, sig);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushTransaction(final OPRETTransaction t) {
|
|
||||||
logger.debug("pushData: {}", t.opretData);
|
|
||||||
transHashMap.put(t.txHash, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void queueOnOPRETRevoke(final byte[] pkhash, final byte[] sig) {
|
|
||||||
for (final ListenerRegistration<OPRETECEventListener> registration : opReturnChangeListeners) {
|
|
||||||
registration.executor.execute(() -> registration.listener.onOPRETRevoke(pkhash, sig));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given event listener object. Returns true if the listener was
|
|
||||||
* removed, false if that listener was never added.
|
|
||||||
*/
|
|
||||||
public boolean removeOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
|
||||||
return ListenerRegistration.removeFromList(listener, opReturnChangeListeners);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ package org.tcpid.opretj;
|
||||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -36,13 +37,13 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
private final OPRETHandlerInterface opbs;
|
private final OPRETHandlerInterface opbs;
|
||||||
private final Logger logger = LoggerFactory.getLogger(OPRETWallet.class);
|
private final Logger logger = LoggerFactory.getLogger(OPRETWallet.class);
|
||||||
|
|
||||||
protected final Map<Sha256Hash, Map<Sha256Hash, OPRETTransaction>> pendingTransactions;
|
protected final Map<Sha256Hash, Map<Sha256Hash, OPRETTransaction>> pendingTransactions = Collections
|
||||||
|
.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
public OPRETWallet(final NetworkParameters params, final KeyChainGroup keyChainGroup,
|
public OPRETWallet(final NetworkParameters params, final KeyChainGroup keyChainGroup,
|
||||||
final OPRETHandlerInterface bs) {
|
final OPRETHandlerInterface bs) {
|
||||||
super(params, keyChainGroup);
|
super(params, keyChainGroup);
|
||||||
opbs = bs;
|
opbs = bs;
|
||||||
pendingTransactions = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,7 +175,7 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
final Sha256Hash h = block.getHeader().getHash();
|
final Sha256Hash h = block.getHeader().getHash();
|
||||||
|
|
||||||
if (!pendingTransactions.containsKey(h)) {
|
if (!pendingTransactions.containsKey(h)) {
|
||||||
pendingTransactions.put(h, new HashMap<Sha256Hash, OPRETTransaction>());
|
pendingTransactions.put(h, Collections.synchronizedMap(new HashMap<Sha256Hash, OPRETTransaction>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingTransactions.get(h).put(tx.getHash(), new OPRETTransaction(h, tx.getHash(), myList));
|
pendingTransactions.get(h).put(tx.getHash(), new OPRETTransaction(h, tx.getHash(), myList));
|
||||||
|
|
Loading…
Reference in a new issue