Use correct crypto curve for DH
This commit is contained in:
parent
f1c7da339c
commit
06556c673a
|
@ -7,7 +7,16 @@
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>opret-testapp</artifactId>
|
<artifactId>opret-testapp</artifactId>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>oss-sonatype</id>
|
||||||
|
<name>oss-sonatype</name>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -29,11 +38,12 @@
|
||||||
<version>1.0.13</version>
|
<version>1.0.13</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.abstractj.kalium</groupId>
|
<groupId>com.github.joshjdevl.libsodiumjni</groupId>
|
||||||
<artifactId>kalium</artifactId>
|
<artifactId>libsodium-jni</artifactId>
|
||||||
<version>0.5.0</version>
|
<version>1.0.2-SNAPSHOT</version>
|
||||||
</dependency>
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.tcpid</groupId>
|
<groupId>org.tcpid</groupId>
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
package org.tcpid.opretj.testapp;
|
package org.tcpid.opretj.testapp;
|
||||||
|
|
||||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
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;
|
||||||
|
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 org.abstractj.kalium.crypto.Box;
|
import org.bitcoinj.core.Address;
|
||||||
import org.abstractj.kalium.crypto.Hash;
|
import org.bitcoinj.core.AddressFormatException;
|
||||||
import org.abstractj.kalium.keys.KeyPair;
|
|
||||||
import org.abstractj.kalium.keys.PublicKey;
|
|
||||||
import org.abstractj.kalium.keys.SigningKey;
|
|
||||||
import org.abstractj.kalium.keys.VerifyKey;
|
|
||||||
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.Sha256Hash;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
|
||||||
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.script.ScriptBuilder;
|
||||||
import org.bitcoinj.utils.Threading;
|
import org.bitcoinj.utils.Threading;
|
||||||
import org.bitcoinj.wallet.KeyChain.KeyPurpose;
|
|
||||||
import org.bitcoinj.wallet.SendRequest;
|
import org.bitcoinj.wallet.SendRequest;
|
||||||
import org.bitcoinj.wallet.Wallet.SendResult;
|
import org.libsodium.jni.crypto.Box;
|
||||||
|
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.opretj.OPRETECParser;
|
import org.tcpid.opretj.OPRETECParser;
|
||||||
|
@ -45,40 +44,46 @@ import joptsimple.OptionSet;
|
||||||
import joptsimple.OptionSpec;
|
import joptsimple.OptionSpec;
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(App.class);
|
public static final Hash HASH = new Hash();
|
||||||
|
public final static long OPRET_BIRTHDAY = 1471989600;
|
||||||
|
|
||||||
public static final long OPRET_BIRTHDAY = 1471989600;
|
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
||||||
static byte[] rawKey = Sha256Hash.hash("TESTSEED".getBytes());
|
private final static SigningKey SK = new SigningKey(HASH.sha256("TESTSEED".getBytes()));
|
||||||
static SigningKey sk = new SigningKey(rawKey);
|
private final static VerifyKey VK = SK.getVerifyKey();
|
||||||
static VerifyKey v = sk.getVerifyKey();
|
private final static byte[] PKHASH = HASH.sha256(VK.toBytes());
|
||||||
private static byte[] pkhash = Sha256Hash.hash(v.toBytes());
|
private final static byte[] REVOKEMSG = Bytes.concat("Revoke ".getBytes(), PKHASH);
|
||||||
static byte[] revokemsg = Bytes.concat("Revoke ".getBytes(), pkhash);
|
|
||||||
|
|
||||||
public static void checkKey(final byte[] pkhash, final byte[] sig) {
|
public static void checkKey(final byte[] pkhash, final byte[] sig) {
|
||||||
logger.warn("CHECKING REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
logger.warn("CHECKING REVOKE PKHASH {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
||||||
|
|
||||||
if (!Arrays.equals(App.pkhash, pkhash)) {
|
if (!Arrays.equals(Arrays.copyOfRange(App.PKHASH, 0, 12), pkhash)) {
|
||||||
logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash));
|
logger.warn("Unknown PK {}", Utils.HEX.encode(pkhash));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.warn("Using VerifyKey {}", v);
|
logger.warn("Using VerifyKey {}", VK);
|
||||||
|
|
||||||
if (v.verify(revokemsg, sig)) {
|
if (VK.verify(REVOKEMSG, sig)) {
|
||||||
logger.warn("REVOKED VerifyKey {}", v);
|
logger.warn("REVOKED VerifyKey {}", VK);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("SIGNATURE does not match!");
|
logger.warn("SIGNATURE does not match!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean cryptoSelfTest() {
|
private static boolean cryptoSelfTest() {
|
||||||
final KeyPair MKpair = new KeyPair();
|
final SigningKey msk = new SigningKey();
|
||||||
final KeyPair VKpair = new KeyPair();
|
|
||||||
|
final KeyPair mkpair = msk.getKeyPair();
|
||||||
|
final KeyPair vkpair = SK.getKeyPair();
|
||||||
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
final byte[] nonce = Arrays.copyOfRange(Sha256Hash.hash("TEST".getBytes()), 0, 8);
|
||||||
final byte[] cipher = doubleEnc(MKpair, VKpair, "TEST".getBytes(), nonce);
|
final byte[] cipher = doubleEnc(mkpair, vkpair, "TEST".getBytes(), nonce);
|
||||||
// System.err.println("Cipher len: " + cipher.length);
|
// System.err.println("Cipher len: " + cipher.length);
|
||||||
final byte[] chk = doubleDec(MKpair.getPublicKey(), VKpair.getPublicKey(), cipher, nonce);
|
try {
|
||||||
|
final byte[] chk = doubleDec(msk.getVerifyKey().getPublicKey(), VK.getPublicKey(), cipher, nonce);
|
||||||
return Arrays.equals(chk, "TEST".getBytes());
|
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) {
|
||||||
|
@ -101,21 +106,21 @@ public class App {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
private static byte[] doubleDec(final PublicKey MK, final PublicKey VK, final byte[] cipher, final byte[] nonce) {
|
||||||
final Hash h = new Hash();
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(
|
final KeyPair Epair = new KeyPair(
|
||||||
Arrays.copyOfRange(h.blake2(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, 32));
|
Arrays.copyOfRange(HASH.sha256(Bytes.concat(nonce, VK.toBytes(), MK.toBytes())), 0, SECRETKEY_BYTES));
|
||||||
|
|
||||||
final Box boxVK = new Box(VK.toBytes(), Epair.getPrivateKey().toBytes());
|
final Box boxVK = new Box(VK, Epair.getPrivateKey());
|
||||||
final byte[] nonceVK = Arrays
|
|
||||||
.copyOfRange(h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VK.toBytes())), 0, 24);
|
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 byte[] cipherMK = boxVK.decrypt(nonceVK, cipher);
|
||||||
|
|
||||||
final Box boxMK = new Box(MK.toBytes(), Epair.getPrivateKey().toBytes());
|
final Box boxMK = new Box(MK, Epair.getPrivateKey());
|
||||||
|
|
||||||
final byte[] nonceMK = Arrays
|
final byte[] nonceMK = Arrays.copyOfRange(
|
||||||
.copyOfRange(h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, 24);
|
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MK.toBytes())), 0, NONCE_BYTES);
|
||||||
|
|
||||||
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
final byte[] clear = boxMK.decrypt(nonceMK, cipherMK);
|
||||||
|
|
||||||
|
@ -124,22 +129,27 @@ public class App {
|
||||||
|
|
||||||
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
private static byte[] doubleEnc(final KeyPair MKpair, final KeyPair VKpair, final byte[] clear,
|
||||||
final byte[] nonce) {
|
final byte[] nonce) {
|
||||||
final Hash h = new Hash();
|
|
||||||
|
|
||||||
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
final KeyPair Epair = new KeyPair(Arrays.copyOfRange(
|
||||||
h.blake2(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
HASH.sha256(Bytes.concat(nonce, VKpair.getPublicKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
||||||
32));
|
SECRETKEY_BYTES));
|
||||||
|
|
||||||
|
final Box boxMK = new Box(Epair.getPublicKey(), MKpair.getPrivateKey());
|
||||||
|
|
||||||
final Box boxMK = new Box(Epair.getPublicKey().toBytes(), MKpair.getPrivateKey().toBytes());
|
|
||||||
final byte[] nonceMK = Arrays.copyOfRange(
|
final byte[] nonceMK = Arrays.copyOfRange(
|
||||||
h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0, 24);
|
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), MKpair.getPublicKey().toBytes())), 0,
|
||||||
|
NONCE_BYTES);
|
||||||
|
|
||||||
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
final byte[] cipherMK = boxMK.encrypt(nonceMK, clear);
|
||||||
|
|
||||||
final Box boxVK = new Box(Epair.getPublicKey().toBytes(), VKpair.getPrivateKey().toBytes());
|
final Box boxVK = new Box(Epair.getPublicKey(), VKpair.getPrivateKey());
|
||||||
|
|
||||||
final byte[] nonceVK = Arrays.copyOfRange(
|
final byte[] nonceVK = Arrays.copyOfRange(
|
||||||
h.blake2(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0, 24);
|
HASH.sha256(Bytes.concat(nonce, Epair.getPrivateKey().toBytes(), VKpair.getPublicKey().toBytes())), 0,
|
||||||
|
NONCE_BYTES);
|
||||||
|
|
||||||
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
final byte[] cipherVK = boxVK.encrypt(nonceVK, cipherMK);
|
||||||
|
|
||||||
return cipherVK;
|
return cipherVK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +177,6 @@ public class App {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleConsole(final OPRETWalletAppKit kit) throws IOException {
|
private static void handleConsole(final OPRETWalletAppKit kit) throws IOException {
|
||||||
final String receiveStr = kit.wallet().freshReceiveAddress().toString();
|
|
||||||
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" };
|
||||||
reader.addCompleter(new StringsCompleter(cmds));
|
reader.addCompleter(new StringsCompleter(cmds));
|
||||||
|
@ -178,13 +187,17 @@ public class App {
|
||||||
displayBalance(kit, out);
|
displayBalance(kit, out);
|
||||||
|
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
final String[] argv = line.split("\\s");
|
String[] argv = line.split("\\s");
|
||||||
if (argv.length == 0) {
|
if (argv.length == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String cmd = argv[0];
|
final String cmd = argv[0];
|
||||||
final String args[] = Arrays.copyOfRange(argv, 1, argv.length);
|
if (cmd.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv = Arrays.copyOfRange(argv, 1, argv.length);
|
||||||
|
|
||||||
switch (cmd.toLowerCase()) {
|
switch (cmd.toLowerCase()) {
|
||||||
case "quit":
|
case "quit":
|
||||||
|
@ -196,6 +209,8 @@ public class App {
|
||||||
displayBalance(kit, out);
|
displayBalance(kit, out);
|
||||||
break;
|
break;
|
||||||
case "receive":
|
case "receive":
|
||||||
|
final String receiveStr = kit.wallet().freshReceiveAddress().toString();
|
||||||
|
|
||||||
out.write("send money to: " + receiveStr + "\n");
|
out.write("send money to: " + receiveStr + "\n");
|
||||||
try {
|
try {
|
||||||
out.write(executeCommand("qrencode -t UTF8 -o - " + receiveStr));
|
out.write(executeCommand("qrencode -t UTF8 -o - " + receiveStr));
|
||||||
|
@ -205,14 +220,31 @@ public class App {
|
||||||
out.flush();
|
out.flush();
|
||||||
break;
|
break;
|
||||||
case "empty":
|
case "empty":
|
||||||
if (args.length != 1) {
|
if (argv.length != 1) {
|
||||||
out.println("empty needs a receive address!");
|
out.println("'empty <address>' needs a valid receive address!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
out.println("'" + argv[0] + "'");
|
||||||
|
out.flush();
|
||||||
|
try {
|
||||||
|
final SendRequest request = SendRequest.emptyWallet(Address.fromBase58(kit.params(), argv[0]));
|
||||||
|
try {
|
||||||
|
kit.wallet().sendCoins(request);
|
||||||
|
} catch (final InsufficientMoneyException e) {
|
||||||
|
out.println(e.getLocalizedMessage());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
} catch (final AddressFormatException e) {
|
||||||
|
out.println(e.getLocalizedMessage());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "opret":
|
case "opret":
|
||||||
sendOPReturn(kit, out);
|
sendOPReturn(kit, out);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
out.println("Unknown command. Use 'help' to display available commands.");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,7 +288,7 @@ public class App {
|
||||||
earliestTime = Utils.currentTimeSeconds();
|
earliestTime = Utils.currentTimeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.addOPRET(pkhash, earliestTime);
|
bs.addOPRET(Arrays.copyOfRange(App.PKHASH, 0, 12), earliestTime);
|
||||||
// bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime);
|
// bs.addOPRET(Sha256Hash.hash("test1".getBytes()), earliestTime);
|
||||||
// bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"),
|
// bs.addOPRET(Utils.HEX.decode("0f490dee643b01b06e0ea84c253a90050a3543cfb7c74319fb47b04afee5b872"),
|
||||||
// earliestTime);
|
// earliestTime);
|
||||||
|
@ -274,7 +306,10 @@ public class App {
|
||||||
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
||||||
kit.connectToLocalHost();
|
kit.connectToLocalHost();
|
||||||
}
|
}
|
||||||
kit.setCheckpoints(App.class.getResourceAsStream("/" + params.getId() + ".checkpoints"));
|
final InputStream is = App.class.getResourceAsStream("/" + params.getId() + ".checkpoints");
|
||||||
|
if (is != null) {
|
||||||
|
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!");
|
||||||
|
@ -292,23 +327,40 @@ public class App {
|
||||||
final OPRETWallet wallet = kit.opretwallet();
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
|
|
||||||
wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
wallet.addCoinsReceivedEventListener((wallet1, tx, prevBalance, newBalance) -> {
|
||||||
System.out.println("-----> coins resceived: " + tx.getHashAsString());
|
final Coin c = tx.getValue(wallet1);
|
||||||
System.out.println("received: " + tx.getValue(wallet1));
|
|
||||||
|
if (c.isPositive()) {
|
||||||
|
System.out.println("-----> coins received: " + tx.getHashAsString());
|
||||||
|
System.out.println("received: " + c);
|
||||||
|
} else {
|
||||||
|
System.out.println("-----> coins sent: " + tx.getHashAsString());
|
||||||
|
System.out.println("sent: " + c.negate().toFriendlyString());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.addCoinsSentEventListener(Threading.SAME_THREAD,
|
/*
|
||||||
(wallet1, tx, prevBalance, newBalance) -> System.out.println("coins sent"));
|
* wallet.addCoinsSentEventListener(Threading.SAME_THREAD, (wallet1, tx,
|
||||||
|
* prevBalance, newBalance) -> { final Coin c = tx.getValue(wallet1);
|
||||||
wallet.addKeyChainEventListener(Threading.SAME_THREAD, keys -> System.out.println("new key added"));
|
*
|
||||||
|
* if (c.isPositive()) { System.out.println("-----> coins received: " +
|
||||||
wallet.addScriptChangeEventListener(Threading.SAME_THREAD,
|
* tx.getHashAsString()); System.out.println("received: " + c); } else {
|
||||||
(wallet1, scripts, isAddingScripts) -> System.out.println("new script added"));
|
* System.out.println("-----> coins sent: " + tx.getHashAsString());
|
||||||
|
* System.out.println("sent: " + c.negate().toFriendlyString()); }
|
||||||
wallet.addTransactionConfidenceEventListener(Threading.SAME_THREAD, (wallet1, tx) -> {
|
*
|
||||||
System.out.println("-----> confidence changed: " + tx.getHashAsString());
|
* });
|
||||||
final TransactionConfidence confidence = tx.getConfidence();
|
*
|
||||||
System.out.println("new block depth: " + confidence.getDepthInBlocks());
|
* wallet.addKeyChainEventListener(Threading.SAME_THREAD, keys ->
|
||||||
});
|
* System.out.println("new key added"));
|
||||||
|
*
|
||||||
|
* wallet.addScriptChangeEventListener(Threading.SAME_THREAD, (wallet1,
|
||||||
|
* scripts, isAddingScripts) -> System.out.println("new script added"));
|
||||||
|
*
|
||||||
|
* wallet.addTransactionConfidenceEventListener(Threading.SAME_THREAD,
|
||||||
|
* (wallet1, tx) -> { System.out.println("-----> confidence changed: " +
|
||||||
|
* tx.getHashAsString()); final TransactionConfidence confidence =
|
||||||
|
* tx.getConfidence(); System.out.println("new block depth: " +
|
||||||
|
* confidence.getDepthInBlocks()); });
|
||||||
|
*/
|
||||||
// wallet.allowSpendingUnconfirmedTransactions();
|
// wallet.allowSpendingUnconfirmedTransactions();
|
||||||
|
|
||||||
handleConsole(kit);
|
handleConsole(kit);
|
||||||
|
@ -322,44 +374,17 @@ public class App {
|
||||||
final OPRETWallet wallet = kit.opretwallet();
|
final OPRETWallet wallet = kit.opretwallet();
|
||||||
final NetworkParameters params = wallet.getNetworkParameters();
|
final NetworkParameters params = wallet.getNetworkParameters();
|
||||||
|
|
||||||
Transaction t = new Transaction(params);
|
final Transaction t = new Transaction(params);
|
||||||
final byte[] sig = sk.sign(revokemsg);
|
final byte[] sig = SK.sign(REVOKEMSG);
|
||||||
|
|
||||||
Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec1d")).data(Utils.HEX.decode("fe"))
|
final Script script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec0f")).data(sig)
|
||||||
.data(pkhash).data(Arrays.copyOfRange(sig, 0, 32)).build();
|
.data(Arrays.copyOfRange(PKHASH, 0, 12)).build();
|
||||||
t.addOutput(Coin.ZERO, script);
|
t.addOutput(Coin.ZERO, script);
|
||||||
t.addOutput(Transaction.DEFAULT_TX_FEE, wallet.freshAddress(KeyPurpose.CHANGE));
|
final SendRequest request = SendRequest.forTx(t);
|
||||||
SendRequest request = SendRequest.forTx(t);
|
|
||||||
request.ensureMinRequiredFee = true;
|
request.ensureMinRequiredFee = true;
|
||||||
SendResult sr = null;
|
request.shuffleOutputs = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sr = wallet.sendCoins(request);
|
wallet.sendCoins(request);
|
||||||
} catch (final InsufficientMoneyException e) {
|
|
||||||
output.println(e.getLocalizedMessage());
|
|
||||||
output.flush();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
logger.debug("SendRequest {}", request);
|
|
||||||
|
|
||||||
script = new ScriptBuilder().op(OP_RETURN).data(Utils.HEX.decode("ec1d")).data(Utils.HEX.decode("ff"))
|
|
||||||
.data(pkhash).data(Arrays.copyOfRange(sig, 32, 64)).build();
|
|
||||||
t = new Transaction(params);
|
|
||||||
|
|
||||||
for (final TransactionOutput out : sr.tx.getOutputs()) {
|
|
||||||
if (out.getValue().compareTo(Transaction.DEFAULT_TX_FEE) == 0) {
|
|
||||||
logger.debug("Add Output: {} of value {}", out, out.getValue());
|
|
||||||
t.addInput(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.addOutput(Coin.ZERO, script);
|
|
||||||
request = SendRequest.forTx(t);
|
|
||||||
request.ensureMinRequiredFee = true;
|
|
||||||
sr = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
sr = wallet.sendCoins(request);
|
|
||||||
} catch (final InsufficientMoneyException e) {
|
} catch (final InsufficientMoneyException e) {
|
||||||
output.println(e.getLocalizedMessage());
|
output.println(e.getLocalizedMessage());
|
||||||
output.flush();
|
output.flush();
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2013 Bruno Oliveira, and individual contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.tcpid.opretj.testapp;
|
||||||
|
|
||||||
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.SECRETKEY_BYTES;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||||
|
|
||||||
|
import org.libsodium.jni.crypto.Random;
|
||||||
|
import org.libsodium.jni.crypto.Util;
|
||||||
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
import org.libsodium.jni.keys.KeyPair;
|
||||||
|
import org.libsodium.jni.keys.PrivateKey;
|
||||||
|
|
||||||
|
public class SigningKey {
|
||||||
|
|
||||||
|
private final byte[] seed;
|
||||||
|
private final byte[] secretKey;
|
||||||
|
|
||||||
|
private final VerifyKey verifyKey;
|
||||||
|
|
||||||
|
public SigningKey() {
|
||||||
|
this(new Random().randomBytes(SECRETKEY_BYTES));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SigningKey(byte[] seed) {
|
||||||
|
Util.checkLength(seed, SECRETKEY_BYTES);
|
||||||
|
this.seed = seed;
|
||||||
|
this.secretKey = Util.zeros(SECRETKEY_BYTES * 2);
|
||||||
|
final byte[] publicKey = Util.zeros(PUBLICKEY_BYTES);
|
||||||
|
Util.isValid(sodium().crypto_sign_ed25519_seed_keypair(publicKey, secretKey, seed),
|
||||||
|
"Failed to generate a key pair");
|
||||||
|
|
||||||
|
this.verifyKey = new VerifyKey(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SigningKey(String seed, Encoder encoder) {
|
||||||
|
this(encoder.decode(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyPair getKeyPair() {
|
||||||
|
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||||
|
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||||
|
return new KeyPair(sk);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrivateKey getPrivateKey() {
|
||||||
|
final byte[] sk = Util.zeros(SECRETKEY_BYTES);
|
||||||
|
sodium().crypto_sign_ed25519_sk_to_curve25519(sk, this.secretKey);
|
||||||
|
return new PrivateKey(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerifyKey getVerifyKey() {
|
||||||
|
return this.verifyKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] sign(byte[] message) {
|
||||||
|
byte[] signature = Util.prependZeros(SIGNATURE_BYTES, message);
|
||||||
|
final int[] bufferLen = new int[1];
|
||||||
|
sodium().crypto_sign_ed25519(signature, bufferLen, message, message.length, secretKey);
|
||||||
|
signature = Util.slice(signature, 0, SIGNATURE_BYTES);
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String sign(String message, Encoder encoder) {
|
||||||
|
final byte[] signature = sign(encoder.decode(message));
|
||||||
|
return encoder.encode(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Encoder.HEX.encode(seed);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2013 Bruno Oliveira, and individual contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.tcpid.opretj.testapp;
|
||||||
|
|
||||||
|
import static org.libsodium.jni.NaCl.sodium;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.PUBLICKEY_BYTES;
|
||||||
|
import static org.libsodium.jni.SodiumConstants.SIGNATURE_BYTES;
|
||||||
|
|
||||||
|
import org.libsodium.jni.crypto.Util;
|
||||||
|
import org.libsodium.jni.encoders.Encoder;
|
||||||
|
import org.libsodium.jni.keys.PublicKey;
|
||||||
|
|
||||||
|
public class VerifyKey {
|
||||||
|
|
||||||
|
private final byte[] key;
|
||||||
|
|
||||||
|
public VerifyKey(byte[] key) {
|
||||||
|
Util.checkLength(key, PUBLICKEY_BYTES);
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VerifyKey(String key, Encoder encoder) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ 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("ec1d"));
|
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 = new HashMap<>();
|
||||||
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>();
|
protected final Map<Sha256Hash, OPRETTransaction> transHashMap = new HashMap<>();
|
||||||
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
private final CopyOnWriteArrayList<ListenerRegistration<OPRETECEventListener>> opReturnChangeListeners = new CopyOnWriteArrayList<>();
|
||||||
|
@ -31,59 +31,43 @@ public class OPRETECParser 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 t) {
|
||||||
final List<List<Byte>> opret_data = new ArrayList<>(t1.opretData);
|
final List<List<Byte>> opret_data = new ArrayList<>(t.opretData);
|
||||||
opret_data.addAll(t2.opretData);
|
|
||||||
logger.debug("checking {}", opret_data);
|
logger.debug("checking {}", opret_data);
|
||||||
|
|
||||||
|
if (opret_data.size() != 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
List<Byte> chunk;
|
List<Byte> chunk;
|
||||||
chunk = opret_data.get(0);
|
chunk = opret_data.get(0);
|
||||||
if (!chunk.equals(OPRET_MAGIC)) {
|
if (!chunk.equals(OPRET_MAGIC)) {
|
||||||
logger.debug("0: != OPRET_MAGIC");
|
logger.debug("chunk 0: != OPRET_MAGIC");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = opret_data.get(1);
|
chunk = opret_data.get(1);
|
||||||
if (chunk.size() != 1) {
|
if ((chunk.size() != 64)) {
|
||||||
logger.debug("1: size != 1");
|
logger.debug("chunk 1 size != 64, but {}", chunk.size());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunk.get(0) == (byte) 0xFE) {
|
chunk = opret_data.get(2);
|
||||||
if (opret_data.size() != 8) {
|
if ((chunk.size() != 12)) {
|
||||||
logger.debug("FE: size != 8");
|
logger.debug("chunk 2 size!= 12 but {} ", chunk.size());
|
||||||
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleRevoke(t1, t2);
|
return handleRevoke(t);
|
||||||
} else {
|
|
||||||
logger.debug("1: != 0xFE");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
private boolean handleRevoke(final OPRETTransaction t) {
|
||||||
}
|
final byte[] pkhash = Bytes.toArray(t.opretData.get(2));
|
||||||
|
final byte[] sig = Bytes.toArray(t.opretData.get(1));
|
||||||
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));
|
logger.debug("REVOKE PK {} - SIG {}", Utils.HEX.encode(pkhash), Utils.HEX.encode(sig));
|
||||||
queueOnOPRETRevoke(pkhash, sig);
|
queueOnOPRETRevoke(pkhash, sig);
|
||||||
|
@ -93,18 +77,7 @@ public class OPRETECParser extends OPRETBaseHandler {
|
||||||
@Override
|
@Override
|
||||||
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
public void pushData(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
||||||
final List<List<Byte>> opret_data) {
|
final List<List<Byte>> opret_data) {
|
||||||
final OPRETTransaction optrans = new OPRETTransaction(blockHash, txHash, txPrevHash, opret_data);
|
checkData(new OPRETTransaction(blockHash, txHash, opret_data));
|
||||||
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
|
@Override
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.tcpid.opretj;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
|
@ -16,14 +15,11 @@ public class OPRETTransaction implements Serializable {
|
||||||
private static final long serialVersionUID = 4234625243756902517L;
|
private static final long serialVersionUID = 4234625243756902517L;
|
||||||
public final Sha256Hash blockHash;
|
public final Sha256Hash blockHash;
|
||||||
public final Sha256Hash txHash;
|
public final Sha256Hash txHash;
|
||||||
public final Set<Sha256Hash> txPrevHash;
|
|
||||||
public final List<List<Byte>> opretData;
|
public final List<List<Byte>> opretData;
|
||||||
|
|
||||||
public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final Set<Sha256Hash> txPrevHash,
|
public OPRETTransaction(final Sha256Hash blockHash, final Sha256Hash txHash, final List<List<Byte>> opret_data) {
|
||||||
final List<List<Byte>> opret_data) {
|
|
||||||
this.blockHash = blockHash;
|
this.blockHash = blockHash;
|
||||||
this.txHash = txHash;
|
this.txHash = txHash;
|
||||||
this.txPrevHash = txPrevHash;
|
|
||||||
this.opretData = opret_data;
|
this.opretData = opret_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,13 +28,6 @@ public class OPRETTransaction implements Serializable {
|
||||||
final StringBuilder buf = new StringBuilder();
|
final StringBuilder buf = new StringBuilder();
|
||||||
buf.append("Received in Block: ").append(blockHash).append("\n");
|
buf.append("Received in Block: ").append(blockHash).append("\n");
|
||||||
buf.append("TX: ").append(txHash).append("\n");
|
buf.append("TX: ").append(txHash).append("\n");
|
||||||
buf.append("TxPrev: ");
|
|
||||||
|
|
||||||
for (final Sha256Hash d : txPrevHash) {
|
|
||||||
buf.append(d.toString());
|
|
||||||
buf.append(" ");
|
|
||||||
}
|
|
||||||
buf.append("\n");
|
|
||||||
buf.append("Data: ");
|
buf.append("Data: ");
|
||||||
for (final List<Byte> d : opretData) {
|
for (final List<Byte> d : opretData) {
|
||||||
buf.append(Utils.HEX.encode(Bytes.toArray(d)));
|
buf.append(Utils.HEX.encode(Bytes.toArray(d)));
|
||||||
|
|
Loading…
Reference in a new issue