- Finally completed the chain_objects enumeration and general encoding rule - Implemented SpendTx and SignedTx... and I *think* they work now (at least my cheesy canned testing says they do) - Finally decided which way I want to deal with trailing spaces in the advanced variants of GajuFormat - Found a roughly system agnostic way to deal with secret key memory and added an api for using that (might need more, but that will be clear soon)
464 lines
20 KiB
Java
464 lines
20 KiB
Java
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Base64;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Arrays;
|
|
import java.math.BigInteger;
|
|
|
|
import swiss.qpq.gajumaru.core.encoding.Base58;
|
|
import swiss.qpq.gajumaru.core.encoding.RLP;
|
|
import swiss.qpq.gajumaru.core.encoding.ApiEncoder;
|
|
import swiss.qpq.gajumaru.core.formatting.GajuFormat;
|
|
import swiss.qpq.gajumaru.core.crypto.Keccak256;
|
|
import swiss.qpq.gajumaru.core.crypto.Blake2b;
|
|
import swiss.qpq.gajumaru.core.crypto.Ed25519;
|
|
import swiss.qpq.gajumaru.core.data.Id;
|
|
import swiss.qpq.gajumaru.core.data.SpendTx;
|
|
import swiss.qpq.gajumaru.core.data.SignedTx;
|
|
import swiss.qpq.gajumaru.core.tools.CryptoUtils;
|
|
|
|
public class Testinator {
|
|
public static void main(String[] args) {
|
|
if (args.length < 2) {
|
|
System.out.println("Error: Provide the test suite name and the required arguments.");
|
|
System.exit(1);
|
|
}
|
|
|
|
try {
|
|
switch (args[0]) {
|
|
case "base64" -> { System.out.print(base64(args[1])); }
|
|
case "base58" -> { System.out.print(base58(args[1])); }
|
|
case "base58_check" -> { System.out.print(base58_check(args[1])); }
|
|
case "rlp" -> { System.out.print(rlp(args[1])); }
|
|
case "rlp_stream" -> { System.out.print(rlp_stream(args[1])); }
|
|
case "rlp_fail" -> { System.out.print(rlp_fail(args[1])); }
|
|
case "gaju_format" -> { System.out.print(gaju_format(args[1])); }
|
|
case "keccak256" -> { System.out.print(keccak256(args[1])); }
|
|
case "blake2b" -> { System.out.print(blake2b(args[1])); }
|
|
case "ed25519" -> { System.out.print(ed25519(args[1])); }
|
|
case "ed25519_verify" -> { System.out.print(ed25519_verify(args[1])); }
|
|
case "api_encode" -> { System.out.print(api_encode(args[1])); }
|
|
case "id_serialization" -> { System.out.print(id_serialization(args[1])); }
|
|
case "spend_tx" -> { System.out.print(spend_tx(args[1])); }
|
|
case "signed_tx" -> { System.out.print(signed_tx(args[1])); }
|
|
case "fe_parity" -> { System.out.print(fe_parity(args[1])); }
|
|
case "reduce_parity" -> { System.out.print(reduce_parity(args[1])); }
|
|
case "smb_parity" -> { System.out.print(smb_parity(args[1])); }
|
|
case "femul_parity" -> { System.out.print(femul_parity(args[1])); }
|
|
case "frombytes_parity" -> { System.out.print(frombytes_parity(args[1])); }
|
|
case "ge_parity" -> { System.out.print(ge_parity(args[1], args[2])); }
|
|
case "keccak" -> {
|
|
byte[] in = CryptoUtils.hexToBin(args[1]);
|
|
System.out.println(CryptoUtils.binToHex(Keccak256.hash(in)));
|
|
}
|
|
case "blake2b_direct" -> {
|
|
byte[] in = CryptoUtils.hexToBin(args[1]);
|
|
System.out.println(CryptoUtils.binToHex(Blake2b.hash(in)));
|
|
}
|
|
case "ak_encode" -> {
|
|
byte[] in = CryptoUtils.hexToBin(args[1]);
|
|
System.out.println(ApiEncoder.encode(ApiEncoder.Type.ACCOUNT_PUBKEY, in));
|
|
}
|
|
case "ed25519_pub" -> {
|
|
byte[] seed = CryptoUtils.hexToBin(args[1]);
|
|
System.out.println(CryptoUtils.binToHex(Ed25519.publicKey(seed)));
|
|
}
|
|
case "id_serialize" -> {
|
|
int tag = Integer.parseInt(args[1]);
|
|
byte[] val = CryptoUtils.hexToBin(args[2]);
|
|
Id id = new Id(Id.Tag.fromValue(tag), val);
|
|
System.out.println(CryptoUtils.binToHex(id.serialize()));
|
|
}
|
|
default -> {
|
|
System.out.println("Error: Unknown test suite or command: " + args[0]);
|
|
System.exit(1);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
System.err.println("Error: " + e.getMessage());
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
}
|
|
}
|
|
|
|
private static String ge_parity(String aHex, String bHex) throws IOException {
|
|
byte[] a = CryptoUtils.hexToBin(aHex);
|
|
byte[] b = CryptoUtils.hexToBin(bHex);
|
|
|
|
Ed25519.Scratch sc = new Ed25519.Scratch();
|
|
Ed25519.Ge p1 = Ed25519.scalarMulBase(a, sc);
|
|
Ed25519.Ge p2 = Ed25519.scalarMulBase(b, sc);
|
|
Ed25519.Ge p3 = new Ed25519.Ge();
|
|
Ed25519.ge_add(p3, p1, p2, sc);
|
|
byte[] res = Ed25519.compress(p3, sc);
|
|
p1.wipe(); p2.wipe(); p3.wipe(); sc.wipe();
|
|
|
|
return CryptoUtils.binToHex(res) + "|||";
|
|
}
|
|
|
|
private static String frombytes_parity(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "frombytes.test");
|
|
Path resPath = Path.of(workingPath, "frombytes.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String hex : lines) {
|
|
if (hex.trim().isEmpty()) continue;
|
|
byte[] in = CryptoUtils.hexToBin(hex);
|
|
long[] h = new long[10];
|
|
Ed25519.fe_frombytes(h, in);
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < 10; i++) {
|
|
if (i > 0) sb.append(",");
|
|
sb.append(h[i]);
|
|
}
|
|
results.add(sb.toString());
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String femul_parity(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "femul.test");
|
|
Path resPath = Path.of(workingPath, "femul.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] parts = line.split("\\|");
|
|
byte[] a = CryptoUtils.hexToBin(parts[0]);
|
|
byte[] b = CryptoUtils.hexToBin(parts[1]);
|
|
|
|
long[] ha = new long[10], hb = new long[10];
|
|
Ed25519.fe_frombytes(ha, a);
|
|
Ed25519.fe_frombytes(hb, b);
|
|
long[] hr = new long[10];
|
|
Ed25519.fe_mul(hr, ha, hb, new long[19]);
|
|
byte[] out = Ed25519.fe_contract(hr);
|
|
|
|
results.add(CryptoUtils.binToHex(out));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String smb_parity(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "smb.test");
|
|
Path resPath = Path.of(workingPath, "smb.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String hex : lines) {
|
|
if (hex.trim().isEmpty()) continue;
|
|
byte[] in = CryptoUtils.hexToBin(hex);
|
|
|
|
Ed25519.Scratch sc = new Ed25519.Scratch();
|
|
Ed25519.Ge p = Ed25519.scalarMulBase(in, sc);
|
|
byte[] out = Ed25519.compress(p, sc);
|
|
p.wipe(); sc.wipe();
|
|
|
|
results.add(CryptoUtils.binToHex(out));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String reduce_parity(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "reduce.test");
|
|
Path resPath = Path.of(workingPath, "reduce.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String hex : lines) {
|
|
if (hex.trim().isEmpty()) continue;
|
|
byte[] in = CryptoUtils.hexToBin(hex);
|
|
|
|
byte[] s = Arrays.copyOf(in, 64);
|
|
Ed25519.reduceScalar(s);
|
|
byte[] out = Arrays.copyOf(s, 32);
|
|
|
|
results.add(CryptoUtils.binToHex(out));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String fe_parity(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "fe.test");
|
|
Path resPath = Path.of(workingPath, "fe.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String hex : lines) {
|
|
if (hex.trim().isEmpty()) continue;
|
|
byte[] in = CryptoUtils.hexToBin(hex);
|
|
|
|
long[] h = new long[10];
|
|
Ed25519.fe_frombytes(h, in);
|
|
byte[] out = Ed25519.fe_contract(h);
|
|
|
|
results.add(CryptoUtils.binToHex(out));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String base64(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "base64.test");
|
|
Path encPath = Path.of(workingPath, "base64.java.txt");
|
|
Path decPath = Path.of(workingPath, "base64.java.back");
|
|
byte[] rawB = Files.readAllBytes(testPath);
|
|
byte[] encB = Base64.getEncoder().encode(rawB);
|
|
Files.write(encPath, encB);
|
|
byte[] readE = Files.readAllBytes(encPath);
|
|
byte[] decB = Base64.getDecoder().decode(readE);
|
|
Files.write(decPath, decB);
|
|
return encPath.toString() + " " + decPath.toString();
|
|
}
|
|
|
|
private static String base58(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "base58.test");
|
|
Path encPath = Path.of(workingPath, "base58.java.txt");
|
|
Path decPath = Path.of(workingPath, "base58.java.back");
|
|
byte[] rawB = Files.readAllBytes(testPath);
|
|
String encS = Base58.encode(rawB);
|
|
Files.write(encPath, encS.getBytes());
|
|
byte[] readE = Files.readAllBytes(encPath);
|
|
String readS = new String(readE);
|
|
byte[] decB = Base58.decode(readS);
|
|
Files.write(decPath, decB);
|
|
return encPath.toString() + " " + decPath.toString();
|
|
}
|
|
|
|
private static String base58_check(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "base58_check.test");
|
|
Path encPath = Path.of(workingPath, "base58_check.java.txt");
|
|
Path decPath = Path.of(workingPath, "base58_check.java.back");
|
|
byte[] rawB = Files.readAllBytes(testPath);
|
|
String encS = Base58.checkEncode(rawB);
|
|
Files.write(encPath, encS.getBytes());
|
|
byte[] readE = Files.readAllBytes(encPath);
|
|
String readS = new String(readE);
|
|
byte[] decB = Base58.checkDecode(readS);
|
|
Files.write(decPath, decB);
|
|
return encPath.toString() + " " + decPath.toString();
|
|
}
|
|
|
|
private static String rlp(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "rlp.test");
|
|
Path resPath = Path.of(workingPath, "rlp.java.back");
|
|
byte[] encB = Files.readAllBytes(testPath);
|
|
|
|
RLP.RLP_Data decRLP = RLP.decode(encB);
|
|
|
|
RLP.RLP_List root = decRLP.asList();
|
|
RLP.RLP_List sub = root.getItems().get(1).asList();
|
|
byte[] anchor = sub.getItems().get(1).asItem().getBytes();
|
|
|
|
byte[] reEnc = RLP.encode(decRLP);
|
|
Files.write(resPath, reEnc);
|
|
|
|
return new String(anchor) + "|" + resPath.toString();
|
|
}
|
|
|
|
private static String rlp_stream(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "rlp_stream.test");
|
|
byte[] buffer = Files.readAllBytes(testPath);
|
|
|
|
List<String> hashes = new ArrayList<>();
|
|
int offset = 0;
|
|
while (offset < buffer.length) {
|
|
RLP.DecodeResult res = RLP.decodeFrom(buffer, offset);
|
|
hashes.add(Integer.toString(res.data().hashCode())); // Just to verify we got objects
|
|
offset += res.consumed();
|
|
}
|
|
return Integer.toString(hashes.size());
|
|
}
|
|
|
|
private static String rlp_fail(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "rlp_fail.test");
|
|
byte[] buffer = Files.readAllBytes(testPath);
|
|
try {
|
|
RLP.decode(buffer);
|
|
return "SUCCESS";
|
|
} catch (RLP.RLPException e) {
|
|
return "RLP_EXCEPTION: " + e.getMessage();
|
|
} catch (Exception e) {
|
|
return "OTHER_EXCEPTION: " + e.getClass().getSimpleName();
|
|
}
|
|
}
|
|
|
|
private static String gaju_format(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "gaju_format.test");
|
|
Path resPath = Path.of(workingPath, "gaju_format.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] p = line.split("\\|");
|
|
String op = p[0];
|
|
switch (op) {
|
|
case "amount" -> {
|
|
GajuFormat.Type style = GajuFormat.Type.valueOf(p[1]);
|
|
GajuFormat.Unit unit = GajuFormat.Unit.valueOf(p[2]);
|
|
char sep = p[3].charAt(0);
|
|
int span = Integer.parseInt(p[4]);
|
|
BigInteger val = new BigInteger(p[5]);
|
|
results.add(GajuFormat.amount(new GajuFormat.FormatSpec(style, unit, sep, span), val));
|
|
}
|
|
case "approx" -> {
|
|
GajuFormat.Type style = GajuFormat.Type.valueOf(p[1]);
|
|
GajuFormat.Unit unit = GajuFormat.Unit.valueOf(p[2]);
|
|
char sep = p[3].charAt(0);
|
|
int span = Integer.parseInt(p[4]);
|
|
BigInteger val = new BigInteger(p[5]);
|
|
int prec = Integer.parseInt(p[6]);
|
|
results.add(GajuFormat.approxAmount(new GajuFormat.FormatSpec(style, unit, sep, span), val, prec));
|
|
}
|
|
case "read" -> {
|
|
byte[] b = GajuFormat.read(p[1]);
|
|
results.add(new BigInteger(b).toString());
|
|
}
|
|
}
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String keccak256(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "keccak256.test");
|
|
Path resPath = Path.of(workingPath, "keccak256.java.txt");
|
|
byte[] input = Files.readAllBytes(testPath);
|
|
byte[] hash = Keccak256.hash(input);
|
|
Files.write(resPath, CryptoUtils.binToHex(hash).getBytes());
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String blake2b(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "blake2b.test");
|
|
Path resPath = Path.of(workingPath, "blake2b.java.txt");
|
|
byte[] input = Files.readAllBytes(testPath);
|
|
byte[] hash = Blake2b.hash(input);
|
|
Files.write(resPath, CryptoUtils.binToHex(hash).getBytes());
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String ed25519(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "ed25519.test");
|
|
Path resPath = Path.of(workingPath, "ed25519.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] parts = line.split("\\|");
|
|
byte[] seed = CryptoUtils.hexToBin(parts[0]);
|
|
byte[] msg = CryptoUtils.hexToBin(parts[1]);
|
|
|
|
byte[] pub = Ed25519.publicKey(seed);
|
|
byte[] sig = Ed25519.sign(seed, msg);
|
|
boolean verify = Ed25519.verify(pub, msg, sig);
|
|
|
|
results.add(CryptoUtils.binToHex(pub) + "|" + CryptoUtils.binToHex(sig) + "|" + verify);
|
|
|
|
CryptoUtils.wipe(seed);
|
|
CryptoUtils.wipe(msg);
|
|
CryptoUtils.wipe(pub);
|
|
CryptoUtils.wipe(sig);
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String ed25519_verify(String workingPath) throws IOException {
|
|
Path seedPath = Path.of(workingPath, "ed25519_seed.test");
|
|
Path msgPath = Path.of(workingPath, "ed25519_msg.test");
|
|
|
|
byte[] seed = CryptoUtils.hexToBin(Files.readString(seedPath).trim());
|
|
byte[] message = Files.readAllBytes(msgPath);
|
|
|
|
byte[] pub = Ed25519.publicKey(seed);
|
|
byte[] sig = Ed25519.sign(seed, message);
|
|
boolean verify = Ed25519.verify(pub, message, sig);
|
|
|
|
return CryptoUtils.binToHex(pub) + "|" + CryptoUtils.binToHex(sig) + "|" + verify;
|
|
}
|
|
|
|
private static String api_encode(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "api_encode.test");
|
|
Path resPath = Path.of(workingPath, "api_encode.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] p = line.split("\\|");
|
|
ApiEncoder.Type type = ApiEncoder.Type.valueOf(p[0]);
|
|
byte[] payload = CryptoUtils.hexToBin(p[1]);
|
|
results.add(ApiEncoder.encode(type, payload));
|
|
if (type == ApiEncoder.Type.ACCOUNT_SECKEY) {
|
|
CryptoUtils.wipe(payload);
|
|
}
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String id_serialization(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "id_serialization.test");
|
|
Path resPath = Path.of(workingPath, "id_serialization.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] p = line.split("\\|");
|
|
Id.Tag tag = Id.Tag.fromValue(Integer.parseInt(p[0]));
|
|
byte[] val = CryptoUtils.hexToBin(p[1]);
|
|
Id id = new Id(tag, val);
|
|
results.add(CryptoUtils.binToHex(id.serialize()));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String spend_tx(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "spend_tx.test");
|
|
Path resPath = Path.of(workingPath, "spend_tx.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] p = line.split("\\|");
|
|
SpendTx tx = new SpendTx(
|
|
Id.deserialize(CryptoUtils.hexToBin(p[0])),
|
|
Id.deserialize(CryptoUtils.hexToBin(p[1])),
|
|
new BigInteger(p[2]),
|
|
new BigInteger(p[3]),
|
|
new BigInteger(p[4]),
|
|
Long.parseLong(p[5]),
|
|
Long.parseLong(p[6]),
|
|
CryptoUtils.hexToBin(p[7])
|
|
);
|
|
results.add(CryptoUtils.binToHex(tx.serialize()));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
|
|
private static String signed_tx(String workingPath) throws IOException {
|
|
Path testPath = Path.of(workingPath, "signed_tx.test");
|
|
Path resPath = Path.of(workingPath, "signed_tx.java.txt");
|
|
List<String> lines = Files.readAllLines(testPath);
|
|
List<String> results = new ArrayList<>();
|
|
for (String line : lines) {
|
|
if (line.trim().isEmpty()) continue;
|
|
String[] p = line.split("\\|");
|
|
List<byte[]> sigs = new ArrayList<>();
|
|
for (String s : p[0].split(",")) {
|
|
sigs.add(CryptoUtils.hexToBin(s));
|
|
}
|
|
SignedTx tx = new SignedTx(sigs, CryptoUtils.hexToBin(p[1]));
|
|
results.add(CryptoUtils.binToHex(tx.serialize()));
|
|
}
|
|
Files.write(resPath, results);
|
|
return resPath.toString();
|
|
}
|
|
}
|