diff --git a/build.xml b/build.xml
index a0a555c..a966899 100644
--- a/build.xml
+++ b/build.xml
@@ -8,8 +8,51 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/aion-types-22a3be9.jar b/lib/aion-types-22a3be9.jar
new file mode 100644
index 0000000..bad1988
Binary files /dev/null and b/lib/aion-types-22a3be9.jar differ
diff --git a/lib/guava-25.1-jre.jar b/lib/guava-25.1-jre.jar
new file mode 100644
index 0000000..babc175
Binary files /dev/null and b/lib/guava-25.1-jre.jar differ
diff --git a/lib/hamcrest-all-1.3.jar b/lib/hamcrest-all-1.3.jar
new file mode 100644
index 0000000..6f62ba0
Binary files /dev/null and b/lib/hamcrest-all-1.3.jar differ
diff --git a/lib/junit-4.12.jar b/lib/junit-4.12.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/lib/junit-4.12.jar differ
diff --git a/lib/util4j-674e4b5.jar b/lib/util4j-674e4b5.jar
new file mode 100644
index 0000000..39f7813
Binary files /dev/null and b/lib/util4j-674e4b5.jar differ
diff --git a/src/module-info.java b/src/module-info.java
index 583ec41..1b0c116 100644
--- a/src/module-info.java
+++ b/src/module-info.java
@@ -3,4 +3,4 @@
requires aion.rlp;
exports main;
-}
\ No newline at end of file
+}
diff --git a/test/main/PrivateKey.java b/test/main/PrivateKey.java
new file mode 100644
index 0000000..c2b6c71
--- /dev/null
+++ b/test/main/PrivateKey.java
@@ -0,0 +1,125 @@
+package main;
+
+import main.crypto.Blake2b;
+import net.i2p.crypto.eddsa.EdDSAPrivateKey;
+import net.i2p.crypto.eddsa.KeyPairGenerator;
+import net.i2p.crypto.eddsa.Utils;
+import org.aion.util.conversions.Hex;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+
+
+public final class PrivateKey {
+ public static final int SIZE = 32;
+
+ private final byte[] key;
+ private final byte[] address;
+
+ /**
+ * Constructs a new private key consisting of the provided bytes.
+ *
+ * @param privateKeyBytes The bytes of the private key.
+ */
+ private PrivateKey(byte[] privateKeyBytes) throws InvalidKeySpecException {
+ if (privateKeyBytes == null) {
+ throw new NullPointerException("private key bytes cannot be null");
+ }
+ if (privateKeyBytes.length != SIZE) {
+ throw new IllegalArgumentException("bytes of a private key must have a length of " + SIZE);
+ }
+ this.key = privateKeyBytes.clone();
+ this.address = deriveAddress(this.key);
+ }
+
+ public static PrivateKey fromBytes(byte[] privateKeyBytes) throws InvalidKeySpecException {
+ return new PrivateKey(privateKeyBytes);
+ }
+
+ public static PrivateKey random() {
+ try {
+ return new PrivateKey(generatePrivateKey());
+ } catch (InvalidKeySpecException e) {
+ // Hiding the checked exception because this should never actually happen here. We have
+ // complete control over these bytes and know they are generated in a sound way.
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ public byte[] copyOfUnderlyingBytes() {
+ return this.key.clone();
+ }
+
+ public byte[] getPublicAionAddress() {
+ return this.address;
+ }
+
+ @Override
+ public String toString() {
+ return "com.theoan.transactionbuilder.main.PrivateKey { 0x" + Hex.toHexString(this.key) + " }";
+ }
+
+ /**
+ * Returns true only if other is a com.theoan.transactionbuilder.main.PrivateKey with the same underlying bytes.
+ *
+ * @param other The other whose equality is to be tested.
+ * @return whether this is equal to other.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof PrivateKey)) {
+ return false;
+ } else if (other == this) {
+ return true;
+ }
+ return Arrays.equals(this.key, ((PrivateKey) other).key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.key);
+ }
+
+ public static byte[] generatePrivateKey() {
+ KeyPairGenerator keyPairGenerator = new KeyPairGenerator();
+ KeyPair pair = keyPairGenerator.generateKeyPair();
+ EdDSAPrivateKey privateKey = (EdDSAPrivateKey) pair.getPrivate();
+ return Utils.hexToBytes(Utils.bytesToHex(privateKey.getEncoded()).substring(32, 96));
+ }
+
+ private static byte[] deriveAddress(byte[] privateKeyBytes) throws InvalidKeySpecException {
+ if (privateKeyBytes == null) {
+ throw new NullPointerException("private key cannot be null");
+ }
+
+ if (privateKeyBytes.length != 32){
+ throw new IllegalArgumentException("private key mute be 32 bytes");
+ }
+
+ EdDSAPrivateKey privateKey = new EdDSAPrivateKey(new PKCS8EncodedKeySpec(addSkPrefix(Utils.bytesToHex(privateKeyBytes))));
+ byte[] publicKeyBytes = privateKey.getAbyte();
+
+ return computeA0Address(publicKeyBytes);
+ }
+
+ private static byte[] addSkPrefix(String skString){
+ String skEncoded = "302e020100300506032b657004220420" + skString;
+ return Utils.hexToBytes(skEncoded);
+ }
+
+ private static byte[] computeA0Address(byte[] publicKey) {
+ byte A0_IDENTIFIER = (byte) 0xa0;
+ ByteBuffer buf = ByteBuffer.allocate(32);
+ buf.put(A0_IDENTIFIER);
+ buf.put(blake256(publicKey), 1, 31);
+ return buf.array();
+ }
+
+ private static byte[] blake256(byte[] input) {
+ Blake2b digest = Blake2b.Digest.newInstance(32);
+ digest.update(input);
+ return digest.digest();
+ }
+}
diff --git a/test/main/VerifierTest.java b/test/main/VerifierTest.java
new file mode 100644
index 0000000..276654c
--- /dev/null
+++ b/test/main/VerifierTest.java
@@ -0,0 +1,60 @@
+package main;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.math.BigInteger;
+
+public class VerifierTest {
+ @Test
+ public void testMatchedSignerRegular() throws Exception{
+ PrivateKey random = PrivateKey.random();
+ byte[] signed = getSignedTransaction(random);
+ Assert.assertTrue(SignedTransactionVerifier.isSignerForRegularTransaction(signed, random.getPublicAionAddress()));
+
+ }
+
+ @Test
+ public void testUnMatchedSignerRegular() throws Exception{
+ PrivateKey expectedSigner = PrivateKey.random();
+ PrivateKey otherSigner = PrivateKey.random();
+ byte[] signed = getSignedTransaction(expectedSigner);
+ Assert.assertFalse(SignedTransactionVerifier.isSignerForRegularTransaction(signed, otherSigner.copyOfUnderlyingBytes()));
+
+ }
+
+ @Test
+ public void testMatchedSignerInvokable() throws Exception{
+ PrivateKey random = PrivateKey.random();
+ byte[] signed = getSignedInvokable(random);
+ Assert.assertTrue(SignedTransactionVerifier.isSignerForInvokableTransaction(signed, random.getPublicAionAddress()));
+
+ }
+
+ @Test
+ public void testUnMatchedSignerInvokable() throws Exception{
+ PrivateKey expectedSigner = PrivateKey.random();
+ PrivateKey otherSigner = PrivateKey.random();
+ byte[] signed = getSignedInvokable(expectedSigner);
+ Assert.assertFalse(SignedTransactionVerifier.isSignerForInvokableTransaction(signed, otherSigner.copyOfUnderlyingBytes()));
+
+ }
+
+ private byte[] getSignedInvokable(PrivateKey privateKey) throws Exception {
+ return new SignedInvokableTransactionBuilder().privateKey(privateKey.copyOfUnderlyingBytes())
+ .executor(PrivateKey.random().getPublicAionAddress())
+ .data(new byte[32])
+ .destination(PrivateKey.random().getPublicAionAddress())
+ .senderNonce(BigInteger.ZERO)
+ .buildSignedInvokableTransaction();
+ }
+
+ private byte[] getSignedTransaction(PrivateKey privateKey) throws Exception {
+ return new SignedTransactionBuilder().privateKey(privateKey.copyOfUnderlyingBytes())
+ .data(new byte[0])
+ .energyLimit(200000)
+ .energyPrice(10000000000L)
+ .senderNonce(BigInteger.ZERO)
+ .buildSignedTransaction();
+ }
+}