factor out revoke checking
This commit is contained in:
parent
06556c673a
commit
9842bc68ef
|
@ -1,9 +1,5 @@
|
||||||
package org.tcpid.opretj.testapp;
|
package org.tcpid.opretj.testapp;
|
||||||
|
|
||||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
|
||||||
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
|
|
||||||
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -17,24 +13,19 @@ 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;
|
||||||
import org.bitcoinj.script.ScriptBuilder;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
import org.bitcoinj.wallet.SendRequest;
|
import org.bitcoinj.wallet.SendRequest;
|
||||||
import org.libsodium.jni.crypto.Box;
|
|
||||||
import org.libsodium.jni.crypto.Hash;
|
import org.libsodium.jni.crypto.Hash;
|
||||||
import org.libsodium.jni.keys.KeyPair;
|
|
||||||
import org.libsodium.jni.keys.PublicKey;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.tcpid.key.SigningKey;
|
||||||
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;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
import com.google.common.util.concurrent.Service;
|
import com.google.common.util.concurrent.Service;
|
||||||
|
|
||||||
import jline.console.ConsoleReader;
|
import jline.console.ConsoleReader;
|
||||||
|
@ -49,42 +40,6 @@ public class App {
|
||||||
|
|
||||||
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 SigningKey SK = new SigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
private final static VerifyKey VK = SK.getVerifyKey();
|
|
||||||
private final static byte[] PKHASH = HASH.sha256(VK.toBytes());
|
|
||||||
private final static byte[] REVOKEMSG = Bytes.concat("Revoke ".getBytes(), PKHASH);
|
|
||||||
|
|
||||||
public static void checkKey(final byte[] pkhash, final byte[] sig) {
|
|
||||||
logger.warn("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
|
||||||
|
|
||||||
if (!Arrays.equals(Arrays.copyOfRange(App.PKHASH, 0, 12), pkhash)) {
|
|
||||||
logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.warn("Using VerifyKey {}", VK);
|
|
||||||
|
|
||||||
if (VK.verify(REVOKEMSG, sig)) {
|
|
||||||
logger.warn("REVOKED VerifyKey {}", VK);
|
|
||||||
} else {
|
|
||||||
logger.warn("SIGNATURE does not match!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean cryptoSelfTest() {
|
|
||||||
final SigningKey msk = new SigningKey();
|
|
||||||
|
|
||||||
final KeyPair mkpair = msk.getKeyPair();
|
|
||||||
final KeyPair vkpair = SK.getKeyPair();
|
|
||||||
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
|
||||||
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
|
||||||
// System.err.println("Cipher len: " + cipher.length);
|
|
||||||
try {
|
|
||||||
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), VK.getPublicKey(), cipher, nonce);
|
|
||||||
return Arrays.equals(chk, "TEST".getBytes());
|
|
||||||
} catch (final Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
||||||
|
@ -105,54 +60,6 @@ public class App {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(
|
|
||||||
Arrays.copyOfRange(HASH.sha256(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, SECRETKEY_BYTES));
|
|
||||||
|
|
||||||
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceVK = Arrays.copyOfRange(
|
|
||||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())), 0, NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
|
||||||
|
|
||||||
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceMK = Arrays.copyOfRange(
|
|
||||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
|
||||||
|
|
||||||
return clear;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
|
||||||
final byte[] nonce) {
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
|
||||||
HASH.sha256(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
|
||||||
SECRETKEY_BYTES));
|
|
||||||
|
|
||||||
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceMK = Arrays.copyOfRange(
|
|
||||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
|
||||||
NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
|
||||||
|
|
||||||
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
|
||||||
|
|
||||||
final byte[] nonceVK = Arrays.copyOfRange(
|
|
||||||
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0,
|
|
||||||
NONCE_BYTES);
|
|
||||||
|
|
||||||
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
|
||||||
|
|
||||||
return cipherVK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String executeCommand(final String command) {
|
private static String executeCommand(final String command) {
|
||||||
|
|
||||||
final StringBuffer output = new StringBuffer();
|
final StringBuffer output = new StringBuffer();
|
||||||
|
@ -251,14 +158,6 @@ public class App {
|
||||||
|
|
||||||
public static void main(final String[] args) throws Exception {
|
public static void main(final String[] args) throws Exception {
|
||||||
|
|
||||||
final boolean chk = cryptoSelfTest();
|
|
||||||
if (chk) {
|
|
||||||
System.err.println("Crypto self test: PASSED");
|
|
||||||
} else {
|
|
||||||
System.err.println("Crypto self test: FAILED");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -277,7 +176,17 @@ public class App {
|
||||||
|
|
||||||
final OPRETECParser bs = new OPRETECParser();
|
final OPRETECParser bs = new OPRETECParser();
|
||||||
|
|
||||||
bs.addOPRETECRevokeEventListener((pkhash, sig) -> checkKey(pkhash, sig));
|
final boolean chk = bs.cryptoSelfTest();
|
||||||
|
if (chk) {
|
||||||
|
System.err.println("Crypto self test: PASSED");
|
||||||
|
} else {
|
||||||
|
System.err.println("Crypto self test: FAILED");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bs.addOPRETECRevokeEventListener((key) -> {
|
||||||
|
System.out.println("Revoked Key: " + Utils.HEX.encode(key.toBytes()));
|
||||||
|
});
|
||||||
|
|
||||||
long earliestTime;
|
long earliestTime;
|
||||||
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
||||||
|
@ -288,10 +197,7 @@ public class App {
|
||||||
earliestTime = Utils.currentTimeSeconds();
|
earliestTime = Utils.currentTimeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.addOPRET(Arrays.copyOfRange(App.PKHASH, 0, 12), earliestTime);
|
bs.addVerifyKey(SK.getVerifyKey(), earliestTime);
|
||||||
// bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime);
|
|
||||||
// bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"),
|
|
||||||
// earliestTime);
|
|
||||||
|
|
||||||
final OPRETWalletAppKit kit = new OPRETWalletAppKit(params, new File("."), "opretwallet" + params.getId(), bs);
|
final OPRETWalletAppKit kit = new OPRETWalletAppKit(params, new File("."), "opretwallet" + params.getId(), bs);
|
||||||
|
|
||||||
|
@ -375,10 +281,7 @@ public class App {
|
||||||
final NetworkParameters params = wallet.getNetworkParameters();
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
final Transaction t = new Transaction(params);
|
final Transaction t = new Transaction(params);
|
||||||
final byte[] sig = SK.sign(REVOKEMSG);
|
final Script script = OPRETECParser.getRevokeScript(SK);
|
||||||
|
|
||||||
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
|
||||||
.data(Arrays.copyOfRange(PKHASH, 0, 12)).build();
|
|
||||||
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;
|
||||||
|
|
120
opretj/pom.xml
120
opretj/pom.xml
|
@ -1,62 +1,70 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
<modelVersion>4.0.0</modelVersion>
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.tcpid</groupId>
|
<parent>
|
||||||
<artifactId>opret-parent</artifactId>
|
<groupId>org.tcpid</groupId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<artifactId>opret-parent</artifactId>
|
||||||
</parent>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<artifactId>opretj</artifactId>
|
</parent>
|
||||||
|
<artifactId>opretj</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bitcoinj</groupId>
|
|
||||||
<artifactId>bitcoinj-core</artifactId>
|
|
||||||
<version>0.14.3</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependencies>
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>3.8.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>org.bitcoinj</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>bitcoinj-core</artifactId>
|
||||||
<version>1.0.13</version>
|
<version>0.14.3</version>
|
||||||
</dependency>
|
<scope>compile</scope>
|
||||||
</dependencies>
|
</dependency>
|
||||||
|
|
||||||
<properties>
|
<dependency>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<groupId>junit</groupId>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<artifactId>junit</artifactId>
|
||||||
</properties>
|
<version>3.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
<build>
|
</dependency>
|
||||||
<pluginManagement>
|
|
||||||
<plugins>
|
<dependency>
|
||||||
<plugin>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<version>1.0.13</version>
|
||||||
<configuration>
|
</dependency>
|
||||||
<source>1.8</source>
|
<dependency>
|
||||||
<target>1.8</target>
|
<groupId>com.github.joshjdevl.libsodiumjni</groupId>
|
||||||
</configuration>
|
<artifactId>libsodium-jni</artifactId>
|
||||||
</plugin>
|
<version>1.0.2-SNAPSHOT</version>
|
||||||
</plugins>
|
<type>jar</type>
|
||||||
</pluginManagement>
|
</dependency>
|
||||||
</build>
|
</dependencies>
|
||||||
<name>opretj</name>
|
|
||||||
<url>https://github.com/bitcoinj/bitcoinj</url>
|
|
||||||
<scm>
|
<properties>
|
||||||
<url>scm:git:https://github.com/haraldh/opretj.git</url>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
</scm>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
<issueManagement>
|
</properties>
|
||||||
<url>https://github.com/haraldh/opretj/issues</url>
|
|
||||||
</issueManagement>
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
<name>opretj</name>
|
||||||
|
<url>https://github.com/bitcoinj/bitcoinj</url>
|
||||||
|
<scm>
|
||||||
|
<url>scm:git:https://github.com/haraldh/opretj.git</url>
|
||||||
|
</scm>
|
||||||
|
<issueManagement>
|
||||||
|
<url>https://github.com/haraldh/opretj/issues</url>
|
||||||
|
</issueManagement>
|
||||||
</project>
|
</project>
|
||||||
</project>
|
</project>
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.tcpid.opretj.testapp;
|
package org.tcpid.key;
|
||||||
|
|
||||||
import static org.libsodium.jni.NaCl.sodium;
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
|
@ -14,23 +14,43 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.tcpid.opretj.testapp;
|
package org.tcpid.key;
|
||||||
|
|
||||||
import static org.libsodium.jni.NaCl.sodium;
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||||
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
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;
|
||||||
import org.libsodium.jni.keys.PublicKey;
|
import org.libsodium.jni.keys.PublicKey;
|
||||||
|
|
||||||
public class VerifyKey {
|
public class VerifyKey implements Comparable<VerifyKey> {
|
||||||
|
public static final Hash HASH = new Hash();
|
||||||
|
|
||||||
private final byte[] key;
|
private final byte[] key;
|
||||||
|
private final byte[] hash;
|
||||||
|
private final byte[] shortHash;
|
||||||
|
private boolean revoked;
|
||||||
|
|
||||||
|
public boolean isRevoked() {
|
||||||
|
return revoked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRevoked(boolean revoked) {
|
||||||
|
if (this.revoked != true) {
|
||||||
|
this.revoked = revoked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public VerifyKey(byte[] key) {
|
public VerifyKey(byte[] key) {
|
||||||
Util.checkLength(key, PUBLICKEY_BYTES);
|
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
this.hash = HASH.sha256(key);
|
||||||
|
this.shortHash = Arrays.copyOfRange(hash, 0, 12);
|
||||||
|
this.revoked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VerifyKey(String key, Encoder encoder) {
|
public VerifyKey(String key, Encoder encoder) {
|
||||||
|
@ -52,6 +72,17 @@ public class VerifyKey {
|
||||||
return Encoder.HEX.encode(key);
|
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) {
|
public boolean verify(byte[] message, byte[] signature) {
|
||||||
Util.checkLength(signature, SIGNATURE_BYTES);
|
Util.checkLength(signature, SIGNATURE_BYTES);
|
||||||
final byte[] sigAndMsg = Util.merge(signature, message);
|
final byte[] sigAndMsg = Util.merge(signature, message);
|
||||||
|
@ -65,4 +96,28 @@ public class VerifyKey {
|
||||||
public boolean verify(String message, String signature, Encoder encoder) {
|
public boolean verify(String message, String signature, Encoder encoder) {
|
||||||
return verify(encoder.decode(message), encoder.decode(signature));
|
return verify(encoder.decode(message), encoder.decode(signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] toHash() {
|
||||||
|
return this.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getShortHash() {
|
||||||
|
return this.shortHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final VerifyKey other) {
|
||||||
|
for (int i = PUBLICKEY_BYTES - 1; i >= 0; i--) {
|
||||||
|
final int thisByte = this.key[i] & 0xff;
|
||||||
|
final int otherByte = other.key[i] & 0xff;
|
||||||
|
if (thisByte > otherByte) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (thisByte < otherByte) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,8 +19,7 @@ public abstract class OPRETBaseHandler implements OPRETHandlerInterface {
|
||||||
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 = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
protected void addOPRET(final byte[] magic, final long earliestTime) {
|
||||||
public 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);
|
||||||
final List<Byte> blist = Bytes.asList(magic);
|
final List<Byte> blist = Bytes.asList(magic);
|
||||||
magicBytes.put(blist, earliestTime);
|
magicBytes.put(blist, earliestTime);
|
||||||
|
@ -68,7 +67,6 @@ public abstract class OPRETBaseHandler implements OPRETHandlerInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeOPRET(final byte[] magic) {
|
public void removeOPRET(final byte[] magic) {
|
||||||
magicBytes.remove(Bytes.asList(magic));
|
magicBytes.remove(Bytes.asList(magic));
|
||||||
queueOnOPRETChanged();
|
queueOnOPRETChanged();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opretj;
|
||||||
|
|
||||||
|
import org.tcpid.key.VerifyKey;
|
||||||
|
|
||||||
public interface OPRETECEventListener {
|
public interface OPRETECEventListener {
|
||||||
void onOPRETRevoke(final byte[] pkhash, final byte[] sig);
|
void onOPRETRevoke(VerifyKey key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||||
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.Set;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
import org.bitcoinj.core.PartialMerkleTree;
|
||||||
|
@ -31,8 +30,7 @@ public class OPRETECExampleParser extends OPRETBaseHandler {
|
||||||
*/
|
*/
|
||||||
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
public void addOPRETECRevokeEventListener(final OPRETECEventListener listener) {
|
||||||
// This is thread safe, so we don't need to take the lock.
|
// This is thread safe, so we don't need to take the lock.
|
||||||
opReturnChangeListeners
|
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
||||||
.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkData(final OPRETTransaction t1, final OPRETTransaction t2) {
|
private boolean checkData(final OPRETTransaction t1, final OPRETTransaction t2) {
|
||||||
|
@ -91,27 +89,9 @@ public class OPRETECExampleParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
public void pushTransaction(final OPRETTransaction t) {
|
||||||
final List<List<Byte>> opret_data) {
|
logger.debug("pushData: {}", t.opretData);
|
||||||
final OPRETTransaction optrans = new OPRETTransaction(blockHash, txHash, txPrevHash, opret_data);
|
transHashMap.put(t.txHash, t);
|
||||||
logger.debug("pushData: {}", optrans);
|
|
||||||
for (final Sha256Hash t : txPrevHash) {
|
|
||||||
if (transHashMap.containsKey(t)) {
|
|
||||||
final OPRETTransaction opprev = transHashMap.get(t);
|
|
||||||
if (checkData(opprev, optrans)) {
|
|
||||||
transHashMap.remove(t);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transHashMap.put(txHash, optrans);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushMerkle(final Sha256Hash blockHash, final PartialMerkleTree partialMerkleTree) {
|
|
||||||
merkleHashMap.put(blockHash, partialMerkleTree);
|
|
||||||
logger.info("block hash {}", blockHash);
|
|
||||||
logger.info("Merkle Tree: {}", partialMerkleTree);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void queueOnOPRETRevoke(final byte[] pkhash, final byte[] sig) {
|
protected void queueOnOPRETRevoke(final byte[] pkhash, final byte[] sig) {
|
||||||
|
|
|
@ -1,27 +1,41 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opretj;
|
||||||
|
|
||||||
|
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.NONCE_BYTES;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
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;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
import org.bitcoinj.core.PartialMerkleTree;
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
|
import org.bitcoinj.script.Script;
|
||||||
|
import org.bitcoinj.script.ScriptBuilder;
|
||||||
import org.bitcoinj.utils.ListenerRegistration;
|
import org.bitcoinj.utils.ListenerRegistration;
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
|
import org.libsodium.jni.crypto.Box;
|
||||||
|
import org.libsodium.jni.keys.KeyPair;
|
||||||
|
import org.libsodium.jni.keys.PublicKey;
|
||||||
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.VerifyKey;
|
||||||
|
|
||||||
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);
|
||||||
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec0f"));
|
private static final List<Byte> OPRET_MAGIC = Bytes.asList(Utils.HEX.decode("ec0f"));
|
||||||
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = new HashMap<>();
|
protected final Map<Sha256Hash, PartialMerkleTree> merkleHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>();
|
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
protected final Map<List<Byte>, List<VerifyKey>> verifyKeys = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,6 +48,100 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
opReturnChangeListeners.add(new ListenerRegistration<OPRETECEventListener>(listener, Threading.SAME_THREAD));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
||||||
|
|
||||||
|
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
||||||
|
Sha256Hash.of(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())).getBytes(), 0, SECRETKEY_BYTES));
|
||||||
|
|
||||||
|
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceVK = Arrays.copyOfRange(
|
||||||
|
Sha256Hash.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())).getBytes(), 0,
|
||||||
|
NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
||||||
|
|
||||||
|
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceMK = Arrays.copyOfRange(
|
||||||
|
Sha256Hash.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())).getBytes(), 0,
|
||||||
|
NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
||||||
|
|
||||||
|
return clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
||||||
|
final byte[] nonce) {
|
||||||
|
|
||||||
|
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(Sha256Hash
|
||||||
|
.of(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())).getBytes(),
|
||||||
|
0, SECRETKEY_BYTES));
|
||||||
|
|
||||||
|
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceMK = Arrays.copyOfRange(Sha256Hash
|
||||||
|
.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())).getBytes(),
|
||||||
|
0, NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
||||||
|
|
||||||
|
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
||||||
|
|
||||||
|
final byte[] nonceVK = Arrays.copyOfRange(Sha256Hash
|
||||||
|
.of(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())).getBytes(),
|
||||||
|
0, NONCE_BYTES);
|
||||||
|
|
||||||
|
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
||||||
|
|
||||||
|
return cipherVK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Script getRevokeScript(SigningKey key) {
|
||||||
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), key.getVerifyKey().toHash());
|
||||||
|
final byte[] sig = key.sign(revokemsg);
|
||||||
|
|
||||||
|
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
||||||
|
.data(key.getVerifyKey().getShortHash()).build();
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkKeyforRevoke(VerifyKey k, final byte[] sig) {
|
||||||
|
logger.debug("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(k.toHash()), Utils.HEX.encode(sig));
|
||||||
|
|
||||||
|
final byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), k.toHash());
|
||||||
|
|
||||||
|
logger.debug("Using VerifyKey {}", k);
|
||||||
|
|
||||||
|
if (k.verify(revokemsg, sig)) {
|
||||||
|
logger.debug("REVOKED VerifyKey {}", k);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.debug("SIGNATURE does not match!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cryptoSelfTest() {
|
||||||
|
final SigningKey sk = new SigningKey();
|
||||||
|
final VerifyKey vk = sk.getVerifyKey();
|
||||||
|
|
||||||
|
final SigningKey msk = new SigningKey();
|
||||||
|
|
||||||
|
final KeyPair mkpair = msk.getKeyPair();
|
||||||
|
final KeyPair vkpair = sk.getKeyPair();
|
||||||
|
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
||||||
|
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
||||||
|
// System.err.println("Cipher len: " + cipher.length);
|
||||||
|
try {
|
||||||
|
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), vk.getPublicKey(), cipher, nonce);
|
||||||
|
return Arrays.equals(chk, "TEST".getBytes());
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkData(final OPRETTransaction t) {
|
private boolean checkData(final OPRETTransaction t) {
|
||||||
final List<List<Byte>> opret_data = new ArrayList<>(t.opretData);
|
final List<List<Byte>> opret_data = new ArrayList<>(t.opretData);
|
||||||
logger.debug("checking {}", opret_data);
|
logger.debug("checking {}", opret_data);
|
||||||
|
@ -62,34 +170,63 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleRevoke(t);
|
return handleRevoke(t);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleRevoke(final OPRETTransaction t) {
|
private boolean handleRevoke(final OPRETTransaction t) {
|
||||||
final byte[] pkhash = Bytes.toArray(t.opretData.get(2));
|
final List<Byte> pkhash = t.opretData.get(2);
|
||||||
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||||
|
if (!verifyKeys.containsKey(pkhash)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
for (final VerifyKey k : verifyKeys.get(t.opretData.get(2))) {
|
||||||
queueOnOPRETRevoke(pkhash, sig);
|
if (checkKeyforRevoke(k, sig)) {
|
||||||
return true;
|
if (k.isRevoked()) {
|
||||||
|
logger.debug("Duplicate REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()),
|
||||||
|
Utils.HEX.encode(sig));
|
||||||
|
} else {
|
||||||
|
k.setRevoked(true);
|
||||||
|
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(k.getShortHash()), Utils.HEX.encode(sig));
|
||||||
|
}
|
||||||
|
queueOnOPRETRevoke(k);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
public void pushTransaction(final OPRETTransaction t) {
|
||||||
final List<List<Byte>> opret_data) {
|
checkData(t);
|
||||||
checkData(new OPRETTransaction(blockHash, txHash, opret_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void addVerifyKey(final VerifyKey key, final long earliestTime) {
|
||||||
public void pushMerkle(final Sha256Hash blockHash, final PartialMerkleTree partialMerkleTree) {
|
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
||||||
merkleHashMap.put(blockHash, partialMerkleTree);
|
if (!verifyKeys.containsKey(hash)) {
|
||||||
logger.info("block hash {}", blockHash);
|
verifyKeys.put(hash, new ArrayList<VerifyKey>());
|
||||||
logger.info("Merkle Tree: {}", partialMerkleTree);
|
}
|
||||||
|
|
||||||
|
verifyKeys.get(hash).add(key);
|
||||||
|
logger.debug("Adding pkhash {}", key.getShortHash());
|
||||||
|
addOPRET(key.getShortHash(), earliestTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void queueOnOPRETRevoke(final byte[] pkhash, final byte[] sig) {
|
public void removeVerifyKey(final VerifyKey key) {
|
||||||
|
final List<Byte> hash = Bytes.asList(key.getShortHash());
|
||||||
|
|
||||||
|
if (!verifyKeys.containsKey(hash)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyKeys.get(hash).remove(key);
|
||||||
|
|
||||||
|
removeOPRET(key.getShortHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void queueOnOPRETRevoke(VerifyKey key) {
|
||||||
for (final ListenerRegistration<OPRETECEventListener> registration : opReturnChangeListeners) {
|
for (final ListenerRegistration<OPRETECEventListener> registration : opReturnChangeListeners) {
|
||||||
registration.executor.execute(() -> registration.listener.onOPRETRevoke(pkhash, sig));
|
registration.executor.execute(() -> registration.listener.onOPRETRevoke(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,9 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
|
|
||||||
public interface OPRETHandlerInterface {
|
public interface OPRETHandlerInterface {
|
||||||
|
|
||||||
void addOPRET(byte[] magic, long earliestTime);
|
// void addOPRET(byte[] magic, long earliestTime);
|
||||||
|
|
||||||
void addOPRETChangeEventListener(Executor executor, OPRETChangeEventListener listener);
|
void addOPRETChangeEventListener(Executor executor, OPRETChangeEventListener listener);
|
||||||
|
|
||||||
|
@ -19,13 +16,10 @@ public interface OPRETHandlerInterface {
|
||||||
|
|
||||||
Set<List<Byte>> getOPRETSet();
|
Set<List<Byte>> getOPRETSet();
|
||||||
|
|
||||||
void pushData(final Sha256Hash h, final Sha256Hash sha256Hash, final Set<Sha256Hash> txprev,
|
// void removeOPRET(byte[] magic);
|
||||||
final List<List<Byte>> myList);
|
|
||||||
|
|
||||||
void pushMerkle(final Sha256Hash sha256Hash, final PartialMerkleTree partialMerkleTree);
|
|
||||||
|
|
||||||
void removeOPRET(byte[] magic);
|
|
||||||
|
|
||||||
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
boolean removeOPRETChangeEventListener(OPRETChangeEventListener listener);
|
||||||
|
|
||||||
|
void pushTransaction(OPRETTransaction t);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package org.tcpid.opretj;
|
package org.tcpid.opretj;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.PartialMerkleTree;
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -15,26 +12,15 @@ public class OPRETSimpleLogger extends OPRETBaseHandler {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(OPRETSimpleLogger.class);
|
private static final Logger logger = LoggerFactory.getLogger(OPRETSimpleLogger.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
public void pushTransaction(final OPRETTransaction t) {
|
||||||
final List<List<Byte>> opret_data) {
|
|
||||||
final StringBuilder buf = new StringBuilder();
|
final StringBuilder buf = new StringBuilder();
|
||||||
final StringBuilder bufPrev = new StringBuilder();
|
|
||||||
|
|
||||||
for (final List<Byte> d : opret_data) {
|
for (final List<Byte> d : t.opretData) {
|
||||||
buf.append(Utils.HEX.encode(Bytes.toArray(d)));
|
buf.append(Utils.HEX.encode(Bytes.toArray(d)));
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Sha256Hash d : txPrevHash) {
|
logger.info("Received in Block: {}\nTX: {}\nData: {}", t.blockHash, t.txHash, buf);
|
||||||
bufPrev.append(d.toString());
|
|
||||||
bufPrev.append(" ");
|
|
||||||
}
|
|
||||||
logger.info("Received in Block: {}\nTX: {}\nTxPrev: {}\nData: {}", blockHash, txHash, bufPrev, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushMerkle(final Sha256Hash blockHash, final PartialMerkleTree partialMerkleTree) {
|
|
||||||
logger.info("block hash {}", blockHash);
|
|
||||||
logger.info("Merkle Tree: {}", partialMerkleTree);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.tcpid.opretj;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.PartialMerkleTree;
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ public class OPRETTransaction implements Serializable {
|
||||||
public final Sha256Hash blockHash;
|
public final Sha256Hash blockHash;
|
||||||
public final Sha256Hash txHash;
|
public final Sha256Hash txHash;
|
||||||
public final List<List<Byte>> opretData;
|
public final List<List<Byte>> opretData;
|
||||||
|
private PartialMerkleTree partialMerkleTree;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -23,6 +25,14 @@ public class OPRETTransaction implements Serializable {
|
||||||
this.opretData = opret_data;
|
this.opretData = opret_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PartialMerkleTree getPartialMerkleTree() {
|
||||||
|
return partialMerkleTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartialMerkleTree(PartialMerkleTree partialMerkleTree) {
|
||||||
|
this.partialMerkleTree = partialMerkleTree;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder buf = new StringBuilder();
|
final StringBuilder buf = new StringBuilder();
|
||||||
|
|
|
@ -4,7 +4,6 @@ import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -19,7 +18,6 @@ import org.bitcoinj.core.ScriptException;
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.StoredBlock;
|
import org.bitcoinj.core.StoredBlock;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionInput;
|
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
import org.bitcoinj.core.TransactionOutput;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
import org.bitcoinj.core.VerificationException;
|
import org.bitcoinj.core.VerificationException;
|
||||||
|
@ -38,14 +36,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);
|
||||||
|
|
||||||
private final Set<Sha256Hash> blocksToStore = new HashSet<>();
|
protected final Map<Sha256Hash, Map<Sha256Hash, OPRETTransaction>> pendingTransactions;
|
||||||
protected final Map<Sha256Hash, Transaction> pendingTransactions;
|
|
||||||
|
|
||||||
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<Sha256Hash, Transaction>();
|
pendingTransactions = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,13 +141,17 @@ 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) {
|
||||||
if (!blocksToStore.contains(block.getHash())) {
|
|
||||||
|
if (!pendingTransactions.containsKey(block.getHash())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
opbs.pushMerkle(block.getHash(), filteredBlock.getPartialMerkleTree());
|
for (final OPRETTransaction t : pendingTransactions.get(block.getHash()).values()) {
|
||||||
|
t.setPartialMerkleTree(filteredBlock.getPartialMerkleTree());
|
||||||
|
opbs.pushTransaction(t);
|
||||||
|
}
|
||||||
|
|
||||||
blocksToStore.remove(block.getHash());
|
pendingTransactions.remove(block.getHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -170,26 +171,13 @@ public class OPRETWallet extends Wallet implements BlocksDownloadedEventListener
|
||||||
logger.debug("False Positive Transaction {}", tx.toString());
|
logger.debug("False Positive Transaction {}", tx.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<Sha256Hash> txprev = new HashSet<>();
|
|
||||||
|
|
||||||
for (final TransactionInput in : tx.getInputs()) {
|
|
||||||
try {
|
|
||||||
final Transaction t = pendingTransactions.get(in.getOutpoint().getHash());
|
|
||||||
txprev.add(t.getHash());
|
|
||||||
pendingTransactions.remove(t);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingTransactions.put(tx.getHash(), tx);
|
|
||||||
|
|
||||||
final Sha256Hash h = block.getHeader().getHash();
|
final Sha256Hash h = block.getHeader().getHash();
|
||||||
if (!blocksToStore.contains(h)) {
|
|
||||||
blocksToStore.add(h);
|
if (!pendingTransactions.containsKey(h)) {
|
||||||
|
pendingTransactions.put(h, new HashMap<Sha256Hash, OPRETTransaction>());
|
||||||
}
|
}
|
||||||
|
|
||||||
opbs.pushData(h, tx.getHash(), txprev, myList);
|
pendingTransactions.get(h).put(tx.getHash(), new OPRETTransaction(h, tx.getHash(), myList));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue