diff --git a/source-code/RuffCT-java/lib/bcprov-jdk15on-156.jar b/source-code/RuffCT-java/lib/bcprov-jdk15on-156.jar
new file mode 100644
index 0000000..ffd08d6
Binary files /dev/null and b/source-code/RuffCT-java/lib/bcprov-jdk15on-156.jar differ
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/crypto/CryptoUtil.java b/source-code/RuffCT-java/src/how/monero/hodl/crypto/CryptoUtil.java
index 266eb04..5b940a6 100644
--- a/source-code/RuffCT-java/src/how/monero/hodl/crypto/CryptoUtil.java
+++ b/source-code/RuffCT-java/src/how/monero/hodl/crypto/CryptoUtil.java
@@ -6,7 +6,6 @@ import org.apache.commons.pool2.BasePooledObjectFactory;
 import org.apache.commons.pool2.PooledObject;
 import org.apache.commons.pool2.impl.DefaultPooledObject;
 import org.apache.commons.pool2.impl.GenericObjectPool;
-import org.nem.core.crypto.ed25519.arithmetic.*;
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
@@ -14,7 +13,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
 
-import static how.monero.hodl.crypto.HashToPoint.hashToPoint;
 import static how.monero.hodl.util.ByteUtil.*;
 
 public class CryptoUtil {
@@ -32,12 +30,6 @@ public class CryptoUtil {
     }
   });
 
-  public static final Ed25519GroupElement G = Ed25519Group.BASE_POINT;
-  public static final Ed25519GroupElement H = new Ed25519EncodedGroupElement(hexToBytes("8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94")).decode();
-  static {
-    H.precomputeForScalarMultiplication();
-  }
-
   public static Scalar hashToScalar(byte[] a) {
     return new Scalar(scReduce32(fastHash(a)));
   }
@@ -89,21 +81,15 @@ public class CryptoUtil {
   }
 
   public static final Random random = new SecureRandom();
-  public static byte[] randomPointAsBytes() {
-    return randomPoint().encode().getRaw();
-  }
-  public static Ed25519GroupElement randomPoint() {
-    return Ed25519Group.BASE_POINT.scalarMultiply(Scalar.randomScalar());
-  }
   public static byte[] randomMessage(int len) {
     byte[] m = new byte[len];
     random.nextBytes(m);
     return m;
   }
 
-  public static byte[] toBytes(Ed25519GroupElement[] a) {
+  public static byte[] toBytes(Curve25519Point[] a) {
     byte[] r = new byte[0];
-    for(Ed25519GroupElement ai : a) r = concat(r, ai.encode().getRaw());
+    for(Curve25519Point ai : a) r = concat(r, ai.toBytes());
     return r;
   }
 
@@ -115,40 +101,39 @@ public class CryptoUtil {
 
 
 
-  public static PointPair COMeg(Scalar xAmount, Scalar rMask) {
-    return new PointPair(G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask).toCached()), G.scalarMultiply(rMask));
+  public static Curve25519PointPair COMeg(Scalar xAmount, Scalar rMask) {
+    return new Curve25519PointPair(Curve25519Point.G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask)), Curve25519Point.G.scalarMultiply(rMask));
   }
 
-  public static Ed25519GroupElement COMp(Scalar xAmount, Scalar rMask) {
-    return G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask).toCached());
+  public static Curve25519Point COMp(Scalar xAmount, Scalar rMask) {
+    return Curve25519Point.G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask));
   }
 
-  public static Ed25519GroupElement COMb(Scalar[][] x, Scalar r) {
+  public static Curve25519Point COMb(Scalar[][] x, Scalar r) {
     int m = x.length;
     int n = x[0].length;
-    Ed25519GroupElement A = G.scalarMultiply(r);
+    Curve25519Point A = Curve25519Point.G.scalarMultiply(r);
     for(int j=0; j<m; j++) {
       for(int i=0; i<n; i++) {
-        A = A.toP3().add(getHpnGLookup(j * n + i + 1).scalarMultiply(x[j][i]).toCached());
+        A = A.add(getHpnGLookup(j * n + i + 1).scalarMultiply(x[j][i]));
       }
     }
     return A;
   }
 
 
-  public static Map<Integer, Ed25519GroupElement> HpnGLookup = new HashMap<>();
+  public static Map<Integer, Curve25519Point> HpnGLookup = new HashMap<>();
 
-  public static Ed25519GroupElement getHpnGLookup(int n) {
+  public static Curve25519Point getHpnGLookup(int n) {
     if(!HpnGLookup.containsKey(n)) {
-      Ed25519GroupElement HpnG = hashToPoint(G.scalarMultiply(Scalar.intToScalar(n)));
-      //HpnG.precomputeForScalarMultiplication(); // try precomputed vs non-precomputed to check best performance
+      Curve25519Point HpnG = Curve25519Point.hashToPoint(Curve25519Point.G.scalarMultiply(Scalar.intToScalar(n)));
       HpnGLookup.put(n, HpnG);
     }
     return HpnGLookup.get(n);
   }
 
-  public static PointPair ENCeg(Ed25519GroupElement X, Scalar r) {
-    return new PointPair(getHpnGLookup(1).scalarMultiply(r).toP3().add(X.toCached()), G.scalarMultiply(r));
+  public static Curve25519PointPair ENCeg(Curve25519Point X, Scalar r) {
+    return new Curve25519PointPair(getHpnGLookup(1).scalarMultiply(r).add(X), Curve25519Point.G.scalarMultiply(r));
   }
 
 
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519Point.java b/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519Point.java
new file mode 100644
index 0000000..df896a3
--- /dev/null
+++ b/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519Point.java
@@ -0,0 +1,98 @@
+package how.monero.hodl.crypto;
+
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.security.Security;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+
+import static how.monero.hodl.crypto.CryptoUtil.hashToScalar;
+import static how.monero.hodl.util.ByteUtil.bytesToHex;
+
+public class Curve25519Point {
+
+  public static ECParameterSpec ecsp;
+
+  static {
+    try {
+      Security.addProvider(new BouncyCastleProvider());
+      ecsp = ECNamedCurveTable.getParameterSpec("curve25519");
+    }
+    catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public ECPoint point;
+  public Curve25519Point(ECPoint point) {
+    this.point = point;
+  }
+  public Curve25519Point(byte[] a) {
+    this.point = ecsp.getCurve().decodePoint(a);
+  }
+
+  public static Curve25519Point randomPoint() {
+    return BASE_POINT.scalarMultiply(Scalar.randomScalar());
+  }
+  public Curve25519Point scalarMultiply(Scalar a) {
+    scalarMults++;
+    if(this==BASE_POINT) scalarBaseMults++;
+
+    if(enableLineRecording) {
+       Optional<StackTraceElement> optionalCaller = Arrays.stream(new Exception().getStackTrace()).filter(e -> e.getFileName().equals(lineRecordingSourceFile)).findFirst();
+       if (optionalCaller.isPresent()) {
+         StackTraceElement caller = optionalCaller.get();
+         lineNumberCallFrequencyMap.putIfAbsent(caller.getLineNumber(), 0);
+         lineNumberCallFrequencyMap.computeIfPresent(caller.getLineNumber(), (key, oldValue) -> oldValue + 1);
+       }
+     }
+
+    return new Curve25519Point(point.multiply(a.toBigInteger()));
+  }
+  public Curve25519Point add(Curve25519Point a) {
+    return new Curve25519Point(point.add(a.point));
+  }
+  public Curve25519Point subtract(Curve25519Point a) {
+    return new Curve25519Point(point.subtract(a.point));
+  }
+  public byte[] toBytes() {
+    return point.getEncoded(true);
+  }
+  public boolean satisfiesCurveEquation() {
+    return true;
+  }
+  public static Curve25519Point hashToPoint(byte[] a) {
+    return BASE_POINT.scalarMultiply(hashToScalar(a));
+  }
+  public static Curve25519Point hashToPoint(Curve25519Point a) {
+    return hashToPoint(a.toBytes());
+  }
+
+  @Override
+  public String toString() {
+    return bytesToHex(toBytes());
+  }
+
+  public static Curve25519Point ZERO = new Curve25519Point(ecsp.getCurve().getInfinity());
+  public static Curve25519Point BASE_POINT = new Curve25519Point(ecsp.getG());
+  public static Curve25519Point G = BASE_POINT;
+
+  public static int scalarMults = 0;
+ 	public static int scalarBaseMults = 0;
+
+  public static String lineRecordingSourceFile = null;
+  public static boolean enableLineRecording = false;
+ 	public static Map<Integer, Integer> lineNumberCallFrequencyMap = new TreeMap<>((a, b)->a.compareTo(b));
+
+
+  @Override
+  public boolean equals(Object obj) {
+    return point.equals(((Curve25519Point) obj).point);
+  }
+}
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519PointPair.java b/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519PointPair.java
new file mode 100644
index 0000000..bf424e7
--- /dev/null
+++ b/source-code/RuffCT-java/src/how/monero/hodl/crypto/Curve25519PointPair.java
@@ -0,0 +1,35 @@
+package how.monero.hodl.crypto;
+
+import static how.monero.hodl.util.ByteUtil.*;
+
+public class Curve25519PointPair {
+  public Curve25519Point P1;
+  public Curve25519Point P2;
+  public Curve25519PointPair(Curve25519Point P1, Curve25519Point P2) {
+    this.P1 = P1;
+    this.P2 = P2;
+  }
+  public byte[] toBytes() {
+    return concat(P1.toBytes(), P2.toBytes());
+  }
+  public Curve25519PointPair add(Curve25519PointPair a) {
+    return new Curve25519PointPair(P1.add(a.P1), P2.add(a.P2));
+  }
+  public Curve25519PointPair subtract(Curve25519PointPair a) {
+    return new Curve25519PointPair(P1.subtract(a.P1), P2.subtract(a.P2));
+  }
+  public Curve25519PointPair multiply(Scalar n) {
+    return new Curve25519PointPair(P1.scalarMultiply(n), P2.scalarMultiply(n));
+  }
+
+  public boolean equals(Curve25519PointPair obj) {
+    return P1.equals(obj.P1) && P2.equals(obj.P2);
+  }
+
+  @Override
+  public String toString() {
+    return "(P1: " + bytesToHex(P1.toBytes()) + ", P2: " + P2 + ")";
+  }
+
+}
+
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/crypto/PointPair.java b/source-code/RuffCT-java/src/how/monero/hodl/crypto/PointPair.java
deleted file mode 100644
index eda3388..0000000
--- a/source-code/RuffCT-java/src/how/monero/hodl/crypto/PointPair.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package how.monero.hodl.crypto;
-
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
-
-import static how.monero.hodl.util.ByteUtil.*;
-
-public class PointPair {
-  public Ed25519GroupElement P1;
-  public Ed25519GroupElement P2;
-  public PointPair(Ed25519GroupElement P1, Ed25519GroupElement P2) {
-    this.P1 = P1;
-    this.P2 = P2;
-  }
-  public byte[] toBytes() {
-    return concat(P1.encode().getRaw(), P2.encode().getRaw());
-  }
-  public PointPair add(PointPair a) {
-    return new PointPair(P1.toP3().add(a.P1.toP3().toCached()), P2.toP3().add(a.P2.toP3().toCached()));
-  }
-  public PointPair subtract(PointPair a) {
-    return new PointPair(P1.toP3().subtract(a.P1.toCached()), P2.toP3().subtract(a.P2.toCached()));
-  }
-  public PointPair multiply(Scalar n) {
-    return new PointPair(P1.toP3().scalarMultiply(n), P2.toP3().scalarMultiply(n));
-  }
-
-  public boolean equals(PointPair obj) {
-    return P1.toP3().equals(obj.P1.toP3()) && P2.toP3().equals(obj.P2.toP3());
-  }
-
-  @Override
-  public String toString() {
-    return "(P1: " + bytesToHex(P1.encode().getRaw()) + ", P2: " + P2 + ")";
-  }
-
-}
-
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/cursor/BootleRuffingCursor.java b/source-code/RuffCT-java/src/how/monero/hodl/cursor/BootleRuffingCursor.java
index 9c932c9..c861147 100644
--- a/source-code/RuffCT-java/src/how/monero/hodl/cursor/BootleRuffingCursor.java
+++ b/source-code/RuffCT-java/src/how/monero/hodl/cursor/BootleRuffingCursor.java
@@ -1,11 +1,8 @@
 package how.monero.hodl.cursor;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519Point;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedFieldElement;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedGroupElement;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519FieldElement;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 public class BootleRuffingCursor extends Cursor {
   public byte[] data;
@@ -14,15 +11,12 @@ public class BootleRuffingCursor extends Cursor {
     super(data);
   }
 
-  public Ed25519GroupElement readGroupElement() {
-    return new Ed25519EncodedGroupElement(readBytes(32)).decode();
+  public Curve25519Point readGroupElement() {
+    return new Curve25519Point(readBytes(32));
   }
-  public Ed25519FieldElement readFieldElement() {
-    return new Ed25519EncodedFieldElement(readBytes(32)).decode();
-  }
-  public PointPair[] readPointPairArray(int len) {
-    PointPair[] result = new PointPair[len];
-    for(int i=0; i<len; i++) result[i] = new PointPair(readGroupElement(), readGroupElement());
+  public Curve25519PointPair[] readPointPairArray(int len) {
+    Curve25519PointPair[] result = new Curve25519PointPair[len];
+    for(int i=0; i<len; i++) result[i] = new Curve25519PointPair(readGroupElement(), readGroupElement());
     return result;
   }
   public Scalar[][] readScalar2DArray(int m, int n) {
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/Multisignature.java b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/Multisignature.java
index 4c0c02f..724babf 100644
--- a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/Multisignature.java
+++ b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/Multisignature.java
@@ -1,7 +1,7 @@
 package how.monero.hodl.ringSignature;
 
+import how.monero.hodl.crypto.Curve25519Point;
 import how.monero.hodl.crypto.Scalar;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 import java.util.*;
 
@@ -12,10 +12,10 @@ import static how.monero.hodl.crypto.CryptoUtil.*;
 
 public class Multisignature {
 
-  public static Ed25519GroupElement[] lexicographicalSort(Ed25519GroupElement[] X) {
-    SortedMap<String, Ed25519GroupElement> hexToPoint = new TreeMap<>();
-    for(Ed25519GroupElement Xi : X) hexToPoint.put(bytesToHex(Xi.encode().getRaw()), Xi);
-    return hexToPoint.values().stream().toArray(Ed25519GroupElement[]::new);
+  public static Curve25519Point[] lexicographicalSort(Curve25519Point[] X) {
+    SortedMap<String, Curve25519Point> hexToPoint = new TreeMap<>();
+    for(Curve25519Point Xi : X) hexToPoint.put(bytesToHex(Xi.toBytes()), Xi);
+    return hexToPoint.values().stream().toArray(Curve25519Point[]::new);
   }
 
   /*
@@ -25,18 +25,18 @@ public class Multisignature {
         2) For each i=1,2,...,n, compute c[i] = Hs(X[i], R, L*, M)
         3) Accept if and only if sG = R + c[1]*X[1] + ... + c[n]*X[n]
    */
-  public static boolean verify(byte[] M, Ed25519GroupElement[] X, Signature signature) {
+  public static boolean verify(byte[] M, Curve25519Point[] X, Signature signature) {
     int n = X.length;
 
     Scalar XAsterisk = hashToScalar(toBytes(lexicographicalSort(X)));
 
     Scalar[] c = new Scalar[n];
     for(int i=0; i<n; i++) {
-      c[i] = hashToScalar(concat(X[i].encode().getRaw(), signature.R.encode().getRaw(), XAsterisk.bytes, M));
+      c[i] = hashToScalar(concat(X[i].toBytes(), signature.R.toBytes(), XAsterisk.bytes, M));
     }
-    Ed25519GroupElement sG = G.scalarMultiply(signature.s);
-    Ed25519GroupElement sG1 = signature.R;
-    for(int i=0; i<n; i++) sG1 = sG1.toP3().add(X[i].scalarMultiply(c[i]).toCached());
+    Curve25519Point sG = Curve25519Point.G.scalarMultiply(signature.s);
+    Curve25519Point sG1 = signature.R;
+    for(int i=0; i<n; i++) sG1 = sG1.add(X[i].scalarMultiply(c[i]));
     return sG.equals(sG1);
   }
 
@@ -53,12 +53,12 @@ public class Multisignature {
       5) Compute s = s[1] + ... + s[n].
       6) Output the signature sigma = (R, s)
    */
-  public static Signature sign(byte[] M, Scalar[] x, Ed25519GroupElement[] X) {
+  public static Signature sign(byte[] M, Scalar[] x, Curve25519Point[] X) {
     int n = x.length;
     if(X==null) {
-      X = new Ed25519GroupElement[n];
+      X = new Curve25519Point[n];
       for(int i=0; i<n; i++) {
-        X[i] = G.scalarMultiply(x[i]);
+        X[i] = Curve25519Point.G.scalarMultiply(x[i]);
       }
 
     }
@@ -68,11 +68,11 @@ public class Multisignature {
     for(int i=0; i<n; i++) rArray[i] = randomScalar();
     Scalar r = sumArray(rArray);
 
-    Ed25519GroupElement R = G.scalarMultiply(r);
+    Curve25519Point R = Curve25519Point.G.scalarMultiply(r);
     Scalar[] c = new Scalar[n];
     Scalar[] sArray = new Scalar[n];
     for(int i=0; i<n; i++) {
-      c[i] = hashToScalar(concat(X[i].encode().getRaw(), R.encode().getRaw(), XAsterisk.bytes, M));
+      c[i] = hashToScalar(concat(X[i].toBytes(), R.toBytes(), XAsterisk.bytes, M));
       sArray[i] = rArray[i].add(x[i].mul(c[i]));
     }
     Scalar s = sumArray(sArray);
@@ -80,13 +80,13 @@ public class Multisignature {
   }
 
   public static class Signature {
-    Ed25519GroupElement R;
+    Curve25519Point R;
     Scalar s;
-    public Signature(Ed25519GroupElement R, Scalar s) {
+    public Signature(Curve25519Point R, Scalar s) {
       this.R = R; this.s = s;
     }
     public byte[] toBytes() {
-      return concat(R.encode().getRaw(), s.bytes);
+      return concat(R.toBytes(), s.bytes);
     }
   }
 
@@ -96,13 +96,13 @@ public class Multisignature {
    */
   public static KeyPair keygen() {
     Scalar x = randomScalar();
-    Ed25519GroupElement X = G.scalarMultiply(x);
+    Curve25519Point X = Curve25519Point.G.scalarMultiply(x);
     return new KeyPair(x, X);
   }
   public static class KeyPair {
     public Scalar x;
-    public Ed25519GroupElement X;
-    public KeyPair(Scalar x, Ed25519GroupElement X) {
+    public Curve25519Point X;
+    public KeyPair(Scalar x, Curve25519Point X) {
       this.x = x; this.X = X;
     }
   }
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/SpendParams.java b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/SpendParams.java
index 2d40c94..b394941 100644
--- a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/SpendParams.java
+++ b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/SpendParams.java
@@ -1,16 +1,16 @@
 package how.monero.hodl.ringSignature;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519Point;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 public class SpendParams {
 
   public int iAsterisk;
-  public PointPair[][] pk;
-  public BootleRuffing.SK[] sk;
-  public Ed25519GroupElement[] ki;
-  public Ed25519GroupElement[] co;
+  public Curve25519PointPair[][] pk;
+  public StringCT.SK[] sk;
+  public Curve25519Point[] ki;
+  public Curve25519Point[] co;
   public byte[] M;
   public Scalar s;
   public int decompositionBase;
diff --git a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/BootleRuffing.java b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/StringCT.java
similarity index 77%
rename from source-code/RuffCT-java/src/how/monero/hodl/ringSignature/BootleRuffing.java
rename to source-code/RuffCT-java/src/how/monero/hodl/ringSignature/StringCT.java
index e8dcd33..05f96a2 100644
--- a/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/BootleRuffing.java
+++ b/source-code/RuffCT-java/src/how/monero/hodl/ringSignature/StringCT.java
@@ -1,23 +1,20 @@
 package how.monero.hodl.ringSignature;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519Point;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
 import how.monero.hodl.cursor.BootleRuffingCursor;
 import how.monero.hodl.util.VarInt;
-import org.nem.core.crypto.ed25519.arithmetic.*;
 
 import java.math.BigInteger;
-import java.util.HashMap;
-import java.util.Map;
 
 import static how.monero.hodl.crypto.CryptoUtil.*;
-import static how.monero.hodl.crypto.HashToPoint.hashToPoint;
 import static how.monero.hodl.crypto.Scalar.bigIntegerArrayToScalarArray;
 import static how.monero.hodl.crypto.Scalar.randomScalar;
 import static how.monero.hodl.util.ByteUtil.*;
 
 
-public class BootleRuffing {
+public class StringCT {
 
   public static class SK {
     public Scalar r;
@@ -34,39 +31,39 @@ public class BootleRuffing {
 
   public static KeyGenResult KEYGEN() {
     SK sk = new SK(randomScalar(), randomScalar());
-    Ed25519GroupElement ki = G.scalarMultiply(sk.r1);
-    PointPair pk = ENCeg(ki, sk.r);
+    Curve25519Point ki = Curve25519Point.G.scalarMultiply(sk.r1);
+    Curve25519PointPair pk = ENCeg(ki, sk.r);
     return new KeyGenResult(sk, ki, pk);
   }
   public static class KeyGenResult {
     public SK sk;
-    public Ed25519GroupElement ki;
-    public PointPair pk = null;
-    public KeyGenResult(SK sk, Ed25519GroupElement ki, PointPair pk) {
+    public Curve25519Point ki;
+    public Curve25519PointPair pk = null;
+    public KeyGenResult(SK sk, Curve25519Point ki, Curve25519PointPair pk) {
       this.sk = sk; this.ki = ki; this.pk = pk;
     }
 
     @Override
     public String toString() {
-      return "sk: " + sk.toString() + ", ki: " + bytesToHex(ki.encode().getRaw()) + ", pk: " + (pk==null ? "(no pk)" : "pk: " + pk);
+      return "sk: " + sk.toString() + ", ki: " + bytesToHex(ki.toBytes()) + ", pk: " + (pk==null ? "(no pk)" : "pk: " + pk);
     }
   }
 
   public static class F {
-    public Ed25519GroupElement[] ki;
-    public PointPair[][] pk;
-    public Ed25519GroupElement[] co;
-    public Ed25519GroupElement co1;
+    public Curve25519Point[] ki;
+    public Curve25519PointPair[][] pk;
+    public Curve25519Point[] co;
+    public Curve25519Point co1;
     byte[] M;
-    public F(Ed25519GroupElement[] ki, PointPair[][] pk, Ed25519GroupElement[] co, Ed25519GroupElement co1, byte[] M) {
+    public F(Curve25519Point[] ki, Curve25519PointPair[][] pk, Curve25519Point[] co, Curve25519Point co1, byte[] M) {
       this.ki = ki; this.pk = pk; this.co = co; this.co1 = co1; this.M = M;
     }
     byte[] toBytes() {
       byte[] r = new byte[0];
-      for(int i=0; i<ki.length; i++) r = concat(r, ki[i].encode().getRaw());
+      for(int i=0; i<ki.length; i++) r = concat(r, ki[i].toBytes());
       for(int i=0; i<pk.length; i++) for(int j=0; j<pk[i].length; j++) r = concat(r, pk[i][j].toBytes());
-      for(int i=0; i<co.length; i++) r = concat(r, co[i].encode().getRaw());
-      r = concat(r, co1.encode().getRaw());
+      for(int i=0; i<co.length; i++) r = concat(r, co[i].toBytes());
+      r = concat(r, co1.toBytes());
       r = concat(r, M);
       return r;
     }
@@ -75,16 +72,16 @@ public class BootleRuffing {
   public static SpendSignature SPEND(SpendParams sp) {
 
     int iAsterisk = sp.iAsterisk;
-    PointPair[][] pk = sp.pk;
+    Curve25519PointPair[][] pk = sp.pk;
     SK[] sk = sp.sk;
-    Ed25519GroupElement[] ki = sp.ki;
-    Ed25519GroupElement[] co = sp.co;
+    Curve25519Point[] ki = sp.ki;
+    Curve25519Point[] co = sp.co;
     byte[] M = sp.M;
     Scalar s = sp.s;
     int decompositionBase = sp.decompositionBase;
     int decompositionExponent = sp.decompositionExponent;
 
-    Ed25519GroupElement co1 = G.scalarMultiply(s);
+    Curve25519Point co1 = Curve25519Point.G.scalarMultiply(s);
     F f = new F(ki, pk, co, co1, M);
     SubResult cf1 = SUB(f);
     Scalar s1 = s;
@@ -101,16 +98,16 @@ public class BootleRuffing {
   public static class SpendSignature {
     public int decompositionBase;
     public int decompositionExponent;
-    public Ed25519GroupElement co1;
+    public Curve25519Point co1;
     public Proof2 sigma1;
     Multisignature.Signature sigma2;
-    public SpendSignature(int decompositionBase, int decompositionExponent, Ed25519GroupElement co1, Proof2 sigma1, Multisignature.Signature sigma2) {
+    public SpendSignature(int decompositionBase, int decompositionExponent, Curve25519Point co1, Proof2 sigma1, Multisignature.Signature sigma2) {
       this.decompositionBase = decompositionBase; this.decompositionExponent = decompositionExponent; this.co1 = co1; this.sigma1 = sigma1; this.sigma2 = sigma2;
     }
     public byte[] toBytes() {
       byte[] result;
       result = concat(VarInt.writeVarInt(decompositionBase), VarInt.writeVarInt(decompositionExponent));
-      result = concat(result, co1.encode().getRaw(), sigma1.toBytes(decompositionBase, decompositionExponent), sigma2.toBytes());
+      result = concat(result, co1.toBytes(), sigma1.toBytes(decompositionBase, decompositionExponent), sigma2.toBytes());
       return result;
     }
     public static SpendSignature fromBytes(byte[] a) {
@@ -131,15 +128,15 @@ public class BootleRuffing {
   public static SubResult SUB(F fin) {
     int L = fin.pk.length; // inputs
     int N = fin.pk[0].length; // ring size
-    PointPair[] pkz = new PointPair[L];
+    Curve25519PointPair[] pkz = new Curve25519PointPair[L];
     Scalar[] f = new Scalar[L];
     for(int j=0; j<L; j++) {
-      pkz[j] = new PointPair(fin.ki[j], Ed25519Group.ZERO_P3);
-      f[j] = hashToScalar(concat(fin.ki[j].encode().getRaw(), fin.toBytes(), longToLittleEndianUint32ByteArray(j)));
+      pkz[j] = new Curve25519PointPair(fin.ki[j], Curve25519Point.ZERO);
+      f[j] = hashToScalar(concat(fin.ki[j].toBytes(), fin.toBytes(), longToLittleEndianUint32ByteArray(j)));
     }
-    PointPair[] c = new PointPair[N];
+    Curve25519PointPair[] c = new Curve25519PointPair[N];
     for(int i=0; i<N; i++) {
-      c[i] = new PointPair(fin.co[i], fin.co1);
+      c[i] = new Curve25519PointPair(fin.co[i], fin.co1);
       for(int j=0; j<L; j++) {
         c[i] = c[i].add( (fin.pk[j][i].subtract(pkz[j])).multiply(f[j]) );
       }
@@ -147,9 +144,9 @@ public class BootleRuffing {
     return new SubResult(c, f);
   }
   public static class SubResult {
-    public PointPair[] c;
+    public Curve25519PointPair[] c;
     public Scalar[] f;
-    public SubResult(PointPair[] c, Scalar[] f) {
+    public SubResult(Curve25519PointPair[] c, Scalar[] f) {
       this.c = c; this.f = f;
     }
   }
@@ -177,7 +174,7 @@ public class BootleRuffing {
       }
     }
 
-    Ed25519GroupElement A = COMb(a, rA);
+    Curve25519Point A = COMb(a, rA);
 
     Scalar[][] c = new Scalar[m][n];
     Scalar[][] d = new Scalar[m][n];
@@ -188,10 +185,10 @@ public class BootleRuffing {
       }
     }
 
-    Ed25519GroupElement C = COMb(c, rC);
-    Ed25519GroupElement D = COMb(d, rD);
+    Curve25519Point C = COMb(c, rC);
+    Curve25519Point D = COMb(d, rD);
 
-    Scalar x = hashToScalar(concat(A.encode().getRaw(), C.encode().getRaw(), D.encode().getRaw()));
+    Scalar x = hashToScalar(concat(A.toBytes(), C.toBytes(), D.toBytes()));
 
     Scalar[][] f = new Scalar[m][n];
     for(int j=0; j<m; j++) {
@@ -214,20 +211,20 @@ public class BootleRuffing {
   }
 
   public static class Proof1 {
-    public Ed25519GroupElement A;
-    public Ed25519GroupElement C;
-    public Ed25519GroupElement D;
+    public Curve25519Point A;
+    public Curve25519Point C;
+    public Curve25519Point D;
     private Scalar[][] fTrimmed;
     private Scalar zA;
     private Scalar zC;
     public transient Scalar[][] a;
 
-    private Proof1(Ed25519GroupElement A, Ed25519GroupElement C, Ed25519GroupElement D, Scalar[][] fTrimmed,
+    private Proof1(Curve25519Point A, Curve25519Point C, Curve25519Point D, Scalar[][] fTrimmed,
                   Scalar zA, Scalar zC, Scalar[][] a) {
       this.A = A; this.C = C; this.D = D; this.fTrimmed = fTrimmed; this.zA = zA; this.zC = zC; this.a = a;
     }
     private byte[] toBytes(int decompositionBase, int decompositionExponent) {
-      byte[] result = concat(A.encode().getRaw(), C.encode().getRaw(), D.encode().getRaw());
+      byte[] result = concat(A.toBytes(), C.toBytes(), D.toBytes());
       for(int j=0; j<decompositionExponent; j++) {
         for(int i=0; i<decompositionBase-1; i++) {
           result = concat(result, fTrimmed[j][i].bytes);
@@ -238,7 +235,7 @@ public class BootleRuffing {
     }
   }
 
-  public static Proof2 PROVE2(PointPair[] co, int iAsterisk, Scalar r, int inputs, int decompositionBase, int decompositionExponent) {
+  public static Proof2 PROVE2(Curve25519PointPair[] co, int iAsterisk, Scalar r, int inputs, int decompositionBase, int decompositionExponent) {
 
     int ringSize = (int) Math.pow(decompositionBase, decompositionExponent);
 
@@ -256,22 +253,22 @@ public class BootleRuffing {
       }
     }
 
-    Ed25519GroupElement B = COMb(d, rB);
+    Curve25519Point B = COMb(d, rB);
 
     Proof1 P = PROVE1(d, rB);
 
     Scalar[][] coefs = COEFS(P.a, iAsterisk);
 
-    PointPair[] G = new PointPair[decompositionExponent];
+    Curve25519PointPair[] G = new Curve25519PointPair[decompositionExponent];
 
     for(int k=0; k<decompositionExponent; k++) {
-      G[k] = ENCeg(Ed25519Group.ZERO_P3, u[k]);
+      G[k] = ENCeg(Curve25519Point.ZERO, u[k]);
       for (int i = 0; i < ringSize; i++) {
         G[k] = G[k].add(co[i].multiply(coefs[i][k]));
       }
     }
 
-    byte[] bytes = concat(P.A.encode().getRaw(), P.C.encode().getRaw(), P.D.encode().getRaw());
+    byte[] bytes = concat(P.A.toBytes(), P.C.toBytes(), P.D.toBytes());
     Scalar x1 = hashToScalar(bytes);
 
     Scalar z = r.mul(x1.pow(decompositionExponent));
@@ -284,11 +281,11 @@ public class BootleRuffing {
 
   public static class Proof2 {
     Proof1 P;
-    public Ed25519GroupElement B;
-    public PointPair[] G;
+    public Curve25519Point B;
+    public Curve25519PointPair[] G;
     public Scalar z;
 
-    private Proof2(Proof1 P, Ed25519GroupElement B, PointPair[] G, Scalar z) {
+    private Proof2(Proof1 P, Curve25519Point B, Curve25519PointPair[] G, Scalar z) {
       this.P = P;
       this.B = B;
       this.G = G;
@@ -297,8 +294,8 @@ public class BootleRuffing {
 
     private byte[] toBytes(int decompositionBase, int decompositionExponent) {
       byte[] bytes;
-      bytes = concat(P.toBytes(decompositionBase, decompositionExponent), B.encode().getRaw());
-      for(PointPair g : G) bytes = concat(bytes, g.toBytes());
+      bytes = concat(P.toBytes(decompositionBase, decompositionExponent), B.toBytes());
+      for(Curve25519PointPair g : G) bytes = concat(bytes, g.toBytes());
       bytes = concat(bytes, z.bytes);
       return bytes;
     }
@@ -321,7 +318,7 @@ public class BootleRuffing {
     return r;
   }
 
-  public static boolean VALID1(Ed25519GroupElement B, Proof1 P) {
+  public static boolean VALID1(Curve25519Point B, Proof1 P) {
     boolean abcdOnCurve =
       P.A.satisfiesCurveEquation()
       && B.satisfiesCurveEquation()
@@ -342,7 +339,7 @@ public class BootleRuffing {
       }
     }
 
-    Scalar x = hashToScalar(concat(P.A.encode().getRaw(), P.C.encode().getRaw(), P.D.encode().getRaw()));
+    Scalar x = hashToScalar(concat(P.A.toBytes(), P.C.toBytes(), P.D.toBytes()));
 
     for(int j=0; j<m; j++) {
       f[j][0] = x;
@@ -369,11 +366,11 @@ public class BootleRuffing {
       }
     }
 
-    if(!B.toP3().scalarMultiply(x).toP3().add(P.A.toP3().toCached()).equals(COMb(f, P.zA))) {
+    if(!B.scalarMultiply(x).add(P.A).equals(COMb(f, P.zA))) {
       System.out.println("VALID1: FAILED xB + A == COMp(f[0][0], ..., f[m-1][n-1]; z[A])");
       return false;
     }
-    if(!P.C.toP3().scalarMultiply(x).toP3().add(P.D.toP3().toCached()).equals(COMb(f1, P.zC))) {
+    if(!P.C.scalarMultiply(x).add(P.D).equals(COMb(f1, P.zC))) {
       System.out.println("VALID1: FAILED xC + D == COMp(f'[0][0], ..., f'[m-1][n-1]; z[C])");
       return false;
     }
@@ -382,7 +379,7 @@ public class BootleRuffing {
 
   }
 
-  public static boolean VALID2(int decompositionBase, Proof2 P1, PointPair[] co) {
+  public static boolean VALID2(int decompositionBase, Proof2 P1, Curve25519PointPair[] co) {
 
     boolean abcdOnCurve =
       P1.P.A.satisfiesCurveEquation()
@@ -399,7 +396,7 @@ public class BootleRuffing {
       return false;
     }
 
-    Scalar x1 = hashToScalar(concat(P1.P.A.encode().getRaw(), P1.P.C.encode().getRaw(), P1.P.D.encode().getRaw()));
+    Scalar x1 = hashToScalar(concat(P1.P.A.toBytes(), P1.P.C.toBytes(), P1.P.D.toBytes()));
 
     int decompositionExponent = P1.P.fTrimmed.length;
     Scalar[][] f = new Scalar[decompositionExponent][decompositionBase];
@@ -411,9 +408,9 @@ public class BootleRuffing {
 
     int ringSize = (int) Math.pow(decompositionBase, decompositionExponent);
 
-    PointPair c = ENCeg(Ed25519Group.ZERO_P3, P1.z);
+    Curve25519PointPair c = ENCeg(Curve25519Point.ZERO, P1.z);
 
-    Scalar x = hashToScalar(concat(P1.P.A.encode().getRaw(), P1.P.C.encode().getRaw(), P1.P.D.encode().getRaw()));
+    Scalar x = hashToScalar(concat(P1.P.A.toBytes(), P1.P.C.toBytes(), P1.P.D.toBytes()));
     for(int j=0; j<decompositionExponent; j++) {
       f[j][0] = x;
       for(int i=1; i<decompositionBase; i++) {
@@ -427,7 +424,7 @@ public class BootleRuffing {
       g[0] = g[0].mul(f[j][0]);
     }
 
-    PointPair c1 = co[0].multiply(g[0]);
+    Curve25519PointPair c1 = co[0].multiply(g[0]);
     for(int i=1; i<ringSize; i++) {
       int[] iSequence = nAryDecompose(decompositionBase, i, decompositionExponent);
       g[i] = f[0][iSequence[0]];
@@ -445,14 +442,14 @@ public class BootleRuffing {
     boolean result = c1.equals(c);
     if(!result) {
       System.out.println("VALID2: FAILED: c' != c");
-      System.out.println("c:  (" + bytesToHex(c.P1.encode().getRaw()) + ", " + bytesToHex(c.P2.encode().getRaw()));
-      System.out.println("c': (" + bytesToHex(c1.P1.encode().getRaw()) + ", " + bytesToHex(c1.P2.encode().getRaw()));
+      System.out.println("c:  (" + bytesToHex(c.P1.toBytes()) + ", " + bytesToHex(c.P2.toBytes()));
+      System.out.println("c': (" + bytesToHex(c1.P1.toBytes()) + ", " + bytesToHex(c1.P2.toBytes()));
     }
     return result;
 
   }
 
-  public static boolean VER(Ed25519GroupElement[] ki, PointPair[][] pk, Ed25519GroupElement[] co, Ed25519GroupElement co1, byte[] M, SpendSignature spendSignature) {
+  public static boolean VER(Curve25519Point[] ki, Curve25519PointPair[][] pk, Curve25519Point[] co, Curve25519Point co1, byte[] M, SpendSignature spendSignature) {
 
     F f = new F(ki, pk, co, co1, M);
 
@@ -473,11 +470,11 @@ public class BootleRuffing {
 
   public static class Output {
     public SK sk;
-    public Ed25519GroupElement ki;
-    public PointPair pk;
+    public Curve25519Point ki;
+    public Curve25519PointPair pk;
 
     public Scalar mask;
-    public Ed25519GroupElement co;
+    public Curve25519Point co;
     public BigInteger amount;
     public static Output genRandomOutput(BigInteger amount) {
       Output o = new Output();
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingBenchmarks.java b/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingBenchmarks.java
index 1db643d..d066fa4 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingBenchmarks.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingBenchmarks.java
@@ -1,7 +1,7 @@
 package test.how.monero.hodl;
 
+import how.monero.hodl.crypto.Curve25519Point;
 import how.monero.hodl.ringSignature.SpendParams;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 import java.io.File;
 import java.io.IOException;
@@ -10,7 +10,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 import static test.how.monero.hodl.BootleRuffingSpendTest.createTestSpendParams;
 
 public class BootleRuffingBenchmarks {
@@ -54,12 +54,12 @@ public class BootleRuffingBenchmarks {
         // create a transaction to spend the outputs, resulting in a signature that proves the authority to send them
         SpendSignature[] spendSignature = new SpendSignature[testIterations];
         for (int i=0; i<testIterations; i++) {
-          Ed25519GroupElement.scalarMults = 0;
-          Ed25519GroupElement.scalarBaseMults = 0;
+          Curve25519Point.scalarMults = 0;
+          Curve25519Point.scalarBaseMults = 0;
           spendSignature[i] = SPEND(sp[i]);
         }
-        int spendScalarMults = Ed25519GroupElement.scalarMults;
-        int spendScalarBaseMults = Ed25519GroupElement.scalarBaseMults;
+        int spendScalarMults = Curve25519Point.scalarMults;
+        int spendScalarBaseMults = Curve25519Point.scalarBaseMults;
 
         long spendSignatureGenerationDuration = (new Date().getTime()-startMs);
         System.out.println("Spend signature generation duration: " + spendSignatureGenerationDuration + " ms");
@@ -72,13 +72,13 @@ public class BootleRuffingBenchmarks {
 
         // verify the spend transaction
         for (int i=0; i<testIterations; i++) {
-          Ed25519GroupElement.scalarMults = 0;
-          Ed25519GroupElement.scalarBaseMults = 0;
+          Curve25519Point.scalarMults = 0;
+          Curve25519Point.scalarBaseMults = 0;
           boolean verified = VER(sp[i].ki, sp[i].pk, sp[i].co, spendSignature[i].co1, sp[i].M, spendSignature[i]);
           System.out.println("verified: " + verified);
         }
-        int verifyScalarMults = Ed25519GroupElement.scalarMults;
-        int verifyScalarBaseMults = Ed25519GroupElement.scalarBaseMults;
+        int verifyScalarMults = Curve25519Point.scalarMults;
+        int verifyScalarBaseMults = Curve25519Point.scalarBaseMults;
 
 
         long spendSignatureVerificationDuration = (new Date().getTime()-startMs);
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingSpendTest.java b/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingSpendTest.java
index d2ff27f..1360f4e 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingSpendTest.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/BootleRuffingSpendTest.java
@@ -1,17 +1,16 @@
 package test.how.monero.hodl;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519Point;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
 import how.monero.hodl.ringSignature.SpendParams;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 import java.math.BigInteger;
 import java.util.Arrays;
 import java.util.Date;
-import java.util.HashMap;
 
 import static how.monero.hodl.crypto.CryptoUtil.*;
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 
 public class BootleRuffingSpendTest {
 
@@ -39,35 +38,35 @@ public class BootleRuffingSpendTest {
 
     // input commitments
     // commitments to the amounts of all inputs referenced in the transaction (real inputs and decoys)
-    Ed25519GroupElement[][] inputCommitments = new Ed25519GroupElement[inputs][ringSize];
+    Curve25519Point[][] inputCommitments = new Curve25519Point[inputs][ringSize];
     for(int j=0; j<inputs; j++) {
       for(int i=0; i<ringSize; i++) {
         if(i==sp.iAsterisk) inputCommitments[j][i] = realInputs[j].co;
-        else inputCommitments[j][i] = randomPoint();
+        else inputCommitments[j][i] = Curve25519Point.randomPoint();
       }
     }
 
     // there is a co commitment for each ring
     // each member of co is sum(COMp(input amt i)) - sum(COMp(output amt i))
-    sp.co = new Ed25519GroupElement[ringSize];
+    sp.co = new Curve25519Point[ringSize];
     for(int i=0; i<ringSize; i++) {
       sp.co[i] = inputCommitments[0][i];
       for(int j=1; j<inputs; j++) {
-        sp.co[i] = sp.co[i].toP3().add(inputCommitments[j][i].toP3().toCached());
+        sp.co[i] = sp.co[i].add(inputCommitments[j][i]);
       }
       for(int k=0; k<outputs.length; k++) {
-        sp.co[i] = sp.co[i].toP3().subtract(outputs[k].co.toP3().toCached());
+        sp.co[i] = sp.co[i].subtract(outputs[k].co);
       }
     }
 
     // the public keys for every input referenced (including real inputs and decoys)
-    sp.pk = new PointPair[inputs][ringSize];
+    sp.pk = new Curve25519PointPair[inputs][ringSize];
 
     // the secret key for every real input referenced
     sp.sk = new SK[inputs];
 
     // the key image for every real input referenced
-    sp.ki = new Ed25519GroupElement[inputs];
+    sp.ki = new Curve25519Point[inputs];
 
     for(int j=0; j<inputs; j++) {
       for(int i=0; i<ringSize; i++) {
@@ -84,14 +83,14 @@ public class BootleRuffingSpendTest {
     for(int i=0; i<realInputs.length; i++) sp.s = sp.s.add(realInputs[i].mask);
     for(int i=0; i<outputs.length; i++) sp.s = sp.s.sub(outputs[i].mask);
 
-    Ed25519GroupElement S = realInputs[0].co;
-    for(int i=1; i<realInputs.length; i++) S = S.toP3().add(realInputs[i].co.toP3().toCached());
-    S = S.toP3().subtract(G.scalarMultiply(new Scalar(fee)).toP3().toCached());
-    for(int i=0; i<outputs.length; i++) S = S.toP3().subtract(outputs[i].co.toP3().toCached());
+    Curve25519Point S = realInputs[0].co;
+    for(int i=1; i<realInputs.length; i++) S = S.add(realInputs[i].co);
+    S = S.subtract(Curve25519Point.G.scalarMultiply(new Scalar(fee)));
+    for(int i=0; i<outputs.length; i++) S = S.subtract(outputs[i].co);
 
-    Ed25519GroupElement S1 = getHpnGLookup(1).scalarMultiply(sp.s);
+    Curve25519Point S1 = getHpnGLookup(1).scalarMultiply(sp.s);
 
-    if(!S.toP3().equals(S1)) throw new RuntimeException("S != S'");
+    if(!S.equals(S1)) throw new RuntimeException("S != S'");
 
     return sp;
   }
@@ -114,8 +113,8 @@ public class BootleRuffingSpendTest {
 
     if(pauseAtEachStage) { System.out.println("Press enter to continue"); try { System.in.read(); } catch (Exception e) {}; System.out.println("Continuing..."); }
 
-    Ed25519GroupElement.scalarMults = 0;
-    Ed25519GroupElement.scalarBaseMults = 0;
+    Curve25519Point.scalarMults = 0;
+    Curve25519Point.scalarBaseMults = 0;
 
     startMs = new Date().getTime();
     // create a transaction to spend the outputs, resulting in a signature that proves the authority to send them
@@ -133,29 +132,29 @@ public class BootleRuffingSpendTest {
     if(pauseAtEachStage) { System.out.println("Press enter to continue"); try { System.in.read(); } catch (Exception e) {}; System.out.println("Continuing..."); }
     startMs = new Date().getTime();
 
-    System.out.println("Spend ScalarMults: " + Ed25519GroupElement.scalarMults);
-    System.out.println("Spend BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
-    Ed25519GroupElement.scalarMults = 0;
-    Ed25519GroupElement.scalarBaseMults = 0;
+    System.out.println("Spend ScalarMults: " + Curve25519Point.scalarMults);
+    System.out.println("Spend BaseScalarMults: " + Curve25519Point.scalarBaseMults);
+    Curve25519Point.scalarMults = 0;
+    Curve25519Point.scalarBaseMults = 0;
 
     //Ed25519GroupElement.enableLineRecording = true;
-    Ed25519GroupElement.lineRecordingSourceFile = "BootleRuffing.java";
+    Curve25519Point.lineRecordingSourceFile = "BootleRuffing.java";
 
     // verify the spend transaction
     for (int i=0; i<testIterations; i++) {
 
-      spendSignature[i] = SpendSignature.fromBytes(spendSignatureBytes[i]);
+      //spendSignature[i] = SpendSignature.fromBytes(spendSignatureBytes[i]);
 
       boolean verified = VER(sp[i].ki, sp[i].pk, sp[i].co, spendSignature[i].co1, sp[i].M, spendSignature[i]);
       System.out.println("verified: " + verified);
     }
 
-    System.out.println("Verify ScalarMults: " + Ed25519GroupElement.scalarMults);
-    System.out.println("Verify BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
+    System.out.println("Verify ScalarMults: " + Curve25519Point.scalarMults);
+    System.out.println("Verify BaseScalarMults: " + Curve25519Point.scalarBaseMults);
 
     System.out.println("Signature verification duration: " + (new Date().getTime()-startMs) + " ms");
 
-    if(Ed25519GroupElement.enableLineRecording) Ed25519GroupElement.lineNumberCallFrequencyMap.entrySet().stream().forEach(e->{System.out.println("line: " + e.getKey() + ", calls: " + e.getValue());});
+    if(Curve25519Point.enableLineRecording) Curve25519Point.lineNumberCallFrequencyMap.entrySet().stream().forEach(e->{System.out.println("line: " + e.getKey() + ", calls: " + e.getValue());});
 
   }
 
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove1Valid1Test1.java b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove1Valid1Test1.java
index 9427c18..a71ff17 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove1Valid1Test1.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove1Valid1Test1.java
@@ -1,10 +1,10 @@
 package test.how.monero.hodl;
 
+import how.monero.hodl.crypto.Curve25519Point;
 import how.monero.hodl.crypto.Scalar;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 import static how.monero.hodl.crypto.CryptoUtil.COMb;
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 
 public class Prove1Valid1Test1 {
 
@@ -19,7 +19,7 @@ public class Prove1Valid1Test1 {
 
     Proof1 P = PROVE1(b, r);
 
-    Ed25519GroupElement B = COMb(b, r);
+    Curve25519Point B = COMb(b, r);
 
     System.out.println("VALID1 returns " + VALID1(B, P));
 
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1.java b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1.java
index a21b373..8a848c3 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1.java
@@ -1,10 +1,10 @@
 package test.how.monero.hodl;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
 
 import static how.monero.hodl.crypto.CryptoUtil.COMeg;
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 
 public class Prove2Valid2Test1 {
 
@@ -16,7 +16,7 @@ public class Prove2Valid2Test1 {
     Scalar r = Scalar.ONE;
     Scalar s = Scalar.ONE;
 
-    PointPair[] co = new PointPair[]{
+    Curve25519PointPair[] co = new Curve25519PointPair[]{
       COMeg(Scalar.ZERO, r),
       COMeg(Scalar.ONE, s)
     };
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1a.java b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1a.java
index 9f0cee7..570fe4e 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1a.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1a.java
@@ -1,10 +1,10 @@
 package test.how.monero.hodl;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
 
 import static how.monero.hodl.crypto.CryptoUtil.COMeg;
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 
 public class Prove2Valid2Test1a {
 
@@ -14,7 +14,7 @@ public class Prove2Valid2Test1a {
 
     Scalar r = Scalar.ONE;
 
-    PointPair[] co = new PointPair[]{
+    Curve25519PointPair[] co = new Curve25519PointPair[]{
       COMeg(Scalar.ONE,Scalar.ONE),
       COMeg(Scalar.ONE,Scalar.ONE),
       COMeg(Scalar.ONE,Scalar.ONE),
diff --git a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1b.java b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1b.java
index 891b464..73e5bcd 100644
--- a/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1b.java
+++ b/source-code/RuffCT-java/src/test/how/monero/hodl/Prove2Valid2Test1b.java
@@ -1,13 +1,13 @@
 package test.how.monero.hodl;
 
-import how.monero.hodl.crypto.PointPair;
+import how.monero.hodl.crypto.Curve25519Point;
+import how.monero.hodl.crypto.Curve25519PointPair;
 import how.monero.hodl.crypto.Scalar;
-import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
 
 import java.util.Date;
 
 import static how.monero.hodl.crypto.CryptoUtil.COMeg;
-import static how.monero.hodl.ringSignature.BootleRuffing.*;
+import static how.monero.hodl.ringSignature.StringCT.*;
 
 public class Prove2Valid2Test1b {
 
@@ -21,7 +21,7 @@ public class Prove2Valid2Test1b {
 
       System.out.println("---------------------------------------------------------------------------");
       int len = (int) Math.pow(2, k);
-      PointPair[] co = new PointPair[len];
+      Curve25519PointPair[] co = new Curve25519PointPair[len];
       for (int i = 0; i < len; i++) co[i] = COMeg(Scalar.intToScalar(i), Scalar.ONE);
 
       int iAsterisk = 0;
@@ -34,23 +34,23 @@ public class Prove2Valid2Test1b {
       System.out.println("decompositionBase: " + decompositionBase);
       System.out.println("decompositionExponent: " + decompositionExponent);
 
-      Ed25519GroupElement.scalarMults = 0;
-      Ed25519GroupElement.scalarBaseMults = 0;
+      Curve25519Point.scalarMults = 0;
+      Curve25519Point.scalarBaseMults = 0;
       long startMs = new Date().getTime();
 
       Proof2 P2 = PROVE2(co, iAsterisk, r, inputs, decompositionBase, decompositionExponent);
 
       System.out.println("PROVE2 duration: " + (new Date().getTime() - startMs) + " ms");
-      System.out.println("PROVE2 ScalarMults: " + Ed25519GroupElement.scalarMults);
-      System.out.println("PROVE2 BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
-      Ed25519GroupElement.scalarMults = 0;
-      Ed25519GroupElement.scalarBaseMults = 0;
+      System.out.println("PROVE2 ScalarMults: " + Curve25519Point.scalarMults);
+      System.out.println("PROVE2 BaseScalarMults: " + Curve25519Point.scalarBaseMults);
+      Curve25519Point.scalarMults = 0;
+      Curve25519Point.scalarBaseMults = 0;
       startMs = new Date().getTime();
 
       System.out.println("VALID2 result: " + VALID2(decompositionBase, P2, co));
 
-      System.out.println("VALID2 ScalarMults: " + Ed25519GroupElement.scalarMults);
-      System.out.println("VALID2 BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
+      System.out.println("VALID2 ScalarMults: " + Curve25519Point.scalarMults);
+      System.out.println("VALID2 BaseScalarMults: " + Curve25519Point.scalarBaseMults);
 
       System.out.println("VALID2 duration: " + (new Date().getTime() - startMs) + " ms");
     }