diff --git a/CLVMDotNet/src/CLVM/CLVMObject.cs b/CLVMDotNet/src/CLVM/CLVMObject.cs
index 9e9b7e7..a85a42e 100644
--- a/CLVMDotNet/src/CLVM/CLVMObject.cs
+++ b/CLVMDotNet/src/CLVM/CLVMObject.cs
@@ -31,7 +31,7 @@ public CLVMObject(dynamic? v)
//to make sure if a tuple is used, it cannot have more than 2 items in it.
var type = v?.GetType();
var s = type?.GetGenericArguments();
- if (s.Length > 2)
+ if (s?.Length > 2)
{
throw new ArgumentException("tuples must be of size 2");
}
diff --git a/CLVMDotNet/src/CLVM/Casts.cs b/CLVMDotNet/src/CLVM/Casts.cs
index 392a111..11ffcd0 100644
--- a/CLVMDotNet/src/CLVM/Casts.cs
+++ b/CLVMDotNet/src/CLVM/Casts.cs
@@ -1,3 +1,4 @@
+using System.Net.Sockets;
using System.Numerics;
namespace CLVMDotNet.CLVM
@@ -15,35 +16,101 @@ public static BigInteger IntFromBytes(byte[] blob)
return new BigInteger(blob, isBigEndian: true);
}
+ ///
+ /// In python integers are dynamically sized, so working out the number of bytes required in
+ /// c# needs to first see if the number will fit into a number of datatypes.
+ ///
+ /// 0-255 (byte)
+ ///
+ ///
+ /// This is required to see if the biginteger parameter is able to fit into a smaller
+ /// datatype and converting them to bytes.
+ ///
+ ///
+ ///
public static byte[] IntToBytes(BigInteger v)
{
- byte[] byteArray = v.ToByteArray();
+ if (v == 0)
+ {
+ byte[] emptyByteArray = new byte[0];
+ return emptyByteArray;
+ }
+
+ //v can fit into a byte 0-255 (unsigned)
+ if (v >= byte.MinValue && v <= byte.MaxValue)
+ {
+ var intValue = (byte)v;
+ var b = (byte)0x00;
+ if (v < 128)
+ {
+ byte[] byteArray = new[] { intValue };
+ return byteArray;
+ }
+ else
+ {
+ byte[] byteArray = new[] { b,intValue };
+ return byteArray;
+ }
+ }
- if (BitConverter.IsLittleEndian)
+ //v can fit into an sbyte -128 to 127 (signed)
+ if (v >= sbyte.MinValue && v <= sbyte.MaxValue)
{
- byteArray = byteArray.Reverse().ToArray();
+ sbyte sbyteValue = (sbyte)v;
+ byte byteValue = (byte)sbyteValue;
+ byte[] byteArray = new[] { byteValue };
+ return byteArray;
}
- while (byteArray.Length > 1 && (byteArray[0] == 0xFF || byteArray[0] == 0x00))
+ //v can fit into a short -32,768 to 32,767 (signed)
+ if (v >= short.MinValue && v <= short.MaxValue)
{
- byteArray = byteArray.Skip(1).ToArray();
+ short shortValue = (short)v;
+ byte[] byteArray = BitConverter.GetBytes(shortValue);
+ Array.Reverse(byteArray);
+ return byteArray;
}
- if (!v.IsZero)
+ //v can fit into a long -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
+ if (v >= long.MinValue && v <= long.MaxValue)
{
- if (byteArray[0] >= 0x80)
+ if (v == 0)
{
- byteArray = new byte[] { 0 }.Concat(byteArray).ToArray();
+ return new byte[0];
}
+
+ byte[] result = v.ToByteArray();
+
+ // if (result[0] == 0x00)
+ // {
+ // // Remove leading 0x00 byte if present
+ // byte[] minimalResult = new byte[result.Length - 1];
+ // Array.Copy(result, 1, minimalResult, 0, minimalResult.Length);
+ // return minimalResult;
+ // }
+ result = result.Reverse().ToArray();
+ return result;
}
+ //python equivalent of numbers larger than a long is a bigInteger
else
{
- byteArray = new byte[0];
- }
+ byte[] byteArray = v.ToByteArray();
+
+ if (BitConverter.IsLittleEndian)
+ {
+ byteArray = byteArray.Reverse().ToArray();
+ }
- return byteArray;
+ while (byteArray.Length > 1 && (byteArray[0] == 0xFF || byteArray[0] == 0x00))
+ {
+ byteArray = byteArray.Skip(1).ToArray();
+ }
+
+ return byteArray;
+ }
}
+
public static int LimbsForInt(BigInteger v)
{
return IntToBytes(v).Length;
diff --git a/CLVMDotNet/src/CLVM/CoreOps.cs b/CLVMDotNet/src/CLVM/CoreOps.cs
index b42d16f..ad27a54 100644
--- a/CLVMDotNet/src/CLVM/CoreOps.cs
+++ b/CLVMDotNet/src/CLVM/CoreOps.cs
@@ -1,8 +1,121 @@
+using System.Numerics;
+
namespace CLVMDotNet.CLVM
{
public class CoreOps
{
- public static Tuple OpIf(SExp args)
+
+ public static IEnumerable ArgsLen(string opName, SExp args)
+ {
+ foreach (var arg in args.AsIter())
+ {
+ if (arg.Pair != null)
+ {
+ throw new EvalError(string.Format("{0} requires int args", opName), arg);
+ }
+ yield return arg.AsAtom().Length;
+ }
+ }
+
+ public static Tuple OpDefaultUnknown(byte[] op, SExp args)
+ {
+ // Any opcode starting with 0xFFFF is reserved (i.e., fatal error).
+ // Opcodes are not allowed to be empty.
+ if (op.Length == 0 || (op[0] == 0xFF && op[1] == 0xFF))
+ {
+ throw new EvalError("reserved operator", SExp.To(op));
+ }
+
+ // All other unknown opcodes are no-ops.
+ // The cost of the no-ops is determined by the opcode number, except the
+ // 6 least significant bits.
+
+ int costFunction = (op[op.Length - 1] & 0b11000000) >> 6;
+ // The multiplier cannot be 0. It starts at 1.
+
+ if (op.Length > 5)
+ {
+ throw new EvalError("invalid operator", SExp.To(op));
+ }
+
+ BigInteger costMultiplier = new BigInteger(op.Take(op.Length - 1).ToArray()) + 1;
+
+ // 0 = constant
+ // 1 = like op_add/op_sub
+ // 2 = like op_multiply
+ // 3 = like op_concat
+ BigInteger cost;
+ switch (costFunction)
+ {
+ case 0:
+ cost = 1;
+ break;
+ case 1:
+ // Like op_add
+ cost = Costs.ARITH_BASE_COST;
+ int argSize = 0;
+ foreach (int l in ArgsLen("unknown op", args))
+ {
+ argSize += l;
+ cost += Costs.ARITH_COST_PER_ARG;
+ }
+
+ cost += argSize * Costs.ARITH_COST_PER_BYTE;
+ break;
+ case 2:
+ // Like op_multiply
+ cost = Costs.MUL_BASE_COST;
+ var operands = ArgsLen("unknown op", args).GetEnumerator();
+ try
+ {
+ int vs = operands.MoveNext() ? operands.Current : 0;
+ while (operands.MoveNext())
+ {
+ int rs = operands.Current;
+ cost += Costs.MUL_COST_PER_OP;
+ cost += (rs + vs) * Costs.MUL_LINEAR_COST_PER_BYTE;
+ cost += (rs * vs) / Costs.MUL_SQUARE_COST_PER_BYTE_DIVIDER;
+ // This is an estimate, since we don't want to actually multiply the values.
+ vs += rs;
+ }
+ }
+ catch (Exception)
+ {
+ // Handle StopIteration
+ }
+
+ break;
+ case 3:
+ // Like concat
+ cost = Costs.CONCAT_BASE_COST;
+ int length = 0;
+ foreach (var arg in args.AsIter())
+ {
+ if (arg.Pair != null)
+ {
+ throw new EvalError("unknown op on list", arg);
+ }
+
+ cost += Costs.CONCAT_COST_PER_ARG;
+ length += arg.Atom.Length;
+ }
+
+ cost += length * Costs.CONCAT_COST_PER_BYTE;
+ break;
+ default:
+ throw new EvalError("invalid operator", SExp.To(op));
+ }
+
+ cost *= (int)costMultiplier;
+ if (cost >= (BigInteger)1 << 32)
+ {
+ throw new EvalError("invalid operator", SExp.To(op));
+ }
+
+ return Tuple.Create(cost, SExp.NULL);
+ }
+
+ public static Tuple OpIf(SExp args)
{
if (args.ListLength() != 3)
{
@@ -12,53 +125,53 @@ public static Tuple OpIf(SExp args)
SExp r = args.Rest();
if (args.First().Nullp())
{
- return new Tuple(Costs.IF_COST, r.Rest().First());
+ return new Tuple(Costs.IF_COST, r.Rest().First());
}
- return new Tuple(Costs.IF_COST, r.First());
+ return new Tuple(Costs.IF_COST, r.First());
}
- public static Tuple OpCons(SExp args)
+ public static Tuple OpCons(SExp args)
{
if (args.ListLength() != 2)
{
throw new EvalError("c takes exactly 2 arguments", args);
}
- return new Tuple(Costs.CONS_COST, args.First().Cons(args.Rest().First()));
+ return new Tuple(Costs.CONS_COST, args.First().Cons(args.Rest().First()));
}
- public static Tuple OpFirst(SExp args)
+ public static Tuple OpFirst(SExp args)
{
if (args.ListLength() != 1)
{
throw new EvalError("f takes exactly 1 argument", args);
}
- return new Tuple(Costs.FIRST_COST, args.First().First());
+ return new Tuple(Costs.FIRST_COST, args.First().First());
}
- public static Tuple OpRest(SExp args)
+ public static Tuple OpRest(SExp args)
{
if (args.ListLength() != 1)
{
throw new EvalError("r takes exactly 1 argument", args);
}
- return new Tuple(Costs.REST_COST, args.First().Rest());
+ return new Tuple(Costs.REST_COST, args.First().Rest());
}
- public static Tuple OpListp(SExp args)
+ public static Tuple OpListp(SExp args)
{
if (args.ListLength() != 1)
{
throw new EvalError("l takes exactly 1 argument", args);
}
- return new Tuple(Costs.LISTP_COST, args.First().Listp() ? SExp.True : SExp.False);
+ return new Tuple(Costs.LISTP_COST, args.First().Listp() ? SExp.True : SExp.False);
}
- public static Tuple OpRaise(SExp args)
+ public static Tuple OpRaise(SExp args)
{
if (args.ListLength() == 1 && !args.First().Listp())
{
@@ -70,7 +183,7 @@ public static Tuple OpRaise(SExp args)
}
}
- public static Tuple OpEq(SExp args)
+ public static Tuple OpEq(SExp args)
{
if (args.ListLength() != 2)
{
@@ -88,10 +201,10 @@ public static Tuple OpEq(SExp args)
byte[] b0 = a0.AsAtom();
byte[] b1 = a1.AsAtom();
- int cost = Costs.EQ_BASE_COST;
- cost += (b0.Length + b1.Length) * Costs.EQ_COST_PER_BYTE;
+ BigInteger cost = Costs.EQ_BASE_COST;
+ cost += (b0!.Length + b1!.Length) * Costs.EQ_COST_PER_BYTE;
- return new Tuple(cost, b0.Equals(b1) ? SExp.True : SExp.False);
+ return Tuple.Create(cost, b0.SequenceEqual(b1) ? SExp.True : SExp.False);
}
}
}
\ No newline at end of file
diff --git a/CLVMDotNet/src/CLVM/HelperFunctions.cs b/CLVMDotNet/src/CLVM/HelperFunctions.cs
index 95438b4..df62a6b 100644
--- a/CLVMDotNet/src/CLVM/HelperFunctions.cs
+++ b/CLVMDotNet/src/CLVM/HelperFunctions.cs
@@ -125,7 +125,9 @@ private static bool IsTuple(dynamic? obj)
continue;
}
- else if (value != null && value.GetType().IsArray && value is not byte[])
+ if (value is System.Collections.IList list &&
+ list.GetType().IsGenericType &&
+ list.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
target = stack.Count;
stack.Add(new CLVMObject(nullBytes));
@@ -205,6 +207,10 @@ public static byte[] ConvertAtomToBytes(dynamic? v)
{
return Encoding.UTF8.GetBytes(str);
}
+ if (v is string[] strarray && strarray.Length == 1)
+ {
+ return Encoding.UTF8.GetBytes(strarray[0]);
+ }
if (v is int intValue)
{
diff --git a/CLVMDotNet/src/CLVM/MoreOps.cs b/CLVMDotNet/src/CLVM/MoreOps.cs
index ff95145..b616732 100644
--- a/CLVMDotNet/src/CLVM/MoreOps.cs
+++ b/CLVMDotNet/src/CLVM/MoreOps.cs
@@ -1,23 +1,28 @@
using System.Numerics;
using System.Security.Cryptography;
+using System.Text;
+using chia.dotnet.bls;
+using dotnetstandard_bip39;
namespace CLVMDotNet.CLVM
{
public static class MoreOps
{
- private const int MALLOC_COST_PER_BYTE = 1;
- private const int SHA256_BASE_COST = 1; // Define other constants as needed.
-
public static Tuple MallocCost(BigInteger cost, SExp atom)
{
- BigInteger newCost = cost + atom.AsAtom().Length * MALLOC_COST_PER_BYTE;
+ var a = atom.AsAtom();
+ var length = a != null ? a.Length : 0;
+
+ BigInteger newCost = cost + length * Costs.MALLOC_COST_PER_BYTE;
return Tuple.Create(newCost, atom);
}
+
public static Tuple OpSha256(SExp args)
{
- int cost = SHA256_BASE_COST;
+ int cost = Costs.SHA256_BASE_COST;
int argLen = 0;
+ byte[] result = Array.Empty();
using (SHA256 sha256 = SHA256.Create())
{
foreach (SExp arg in args.AsIter())
@@ -30,18 +35,16 @@ public static Tuple OpSha256(SExp args)
argLen += atom.Length;
cost += Costs.SHA256_COST_PER_ARG;
- sha256.TransformBlock(atom, 0, atom.Length, null, 0);
+ result = sha256.ComputeHash(atom);
+ cost += argLen * Costs.SHA256_COST_PER_BYTE;
}
- sha256.TransformFinalBlock(Array.Empty(), 0, 0);
- byte[] result = sha256.Hash;
- cost += argLen * Costs.SHA256_COST_PER_BYTE;
-
- return MallocCost(cost, SExp.To(result));
+ var sexp = SExp.To(result);
+ return MallocCost(cost, sexp);
}
}
- public static IEnumerable<(BigInteger, int)> ArgsAsInts(string opName, SExp args)
+ public static IEnumerable<(BigInteger, BigInteger)> ArgsAsInts(string opName, SExp args)
{
foreach (SExp arg in args.AsIter())
{
@@ -51,7 +54,7 @@ public static Tuple OpSha256(SExp args)
}
BigInteger intValue = arg.AsInt();
- int atomLength = arg.AsAtom().Length;
+ int atomLength = arg.AsAtom()!.Length;
yield return (intValue, atomLength);
}
@@ -61,12 +64,13 @@ public static IEnumerable ArgsAsInt32(string opName, SExp args)
{
foreach (SExp arg in args.AsIter())
{
- if (arg != null)
+ if (arg.Pair != null)
{
throw new EvalError($"{opName} requires int32 args", arg);
}
- if (arg.AsAtom().Length > 4)
+ var atom = arg.AsAtom();
+ if (atom?.Length > 4)
{
throw new EvalError($"{opName} requires int32 args (with no leading zeros)", arg);
}
@@ -77,14 +81,18 @@ public static IEnumerable ArgsAsInt32(string opName, SExp args)
public static List<(BigInteger, BigInteger)> ArgsAsIntList(string opName, dynamic args, int count)
{
- var intList = ArgsAsInts(opName, args);
- if (intList.Count != count)
+ List<(BigInteger, BigInteger)> result = new List<(BigInteger, BigInteger)>();
+ foreach (var intList in ArgsAsInts(opName, args))
{
- string plural = count != 1 ? "s" : "";
- throw new EvalError($"{opName} takes exactly {count} argument{plural}", args);
+ result.Add(intList);
}
- return intList;
+ if (result.Count != count)
+ {
+ throw new EvalError($"{opName} takes exactly {count} arguments", args);
+ }
+
+ return result;
}
public static IEnumerable ArgsAsBools(string opName, SExp args)
@@ -108,13 +116,70 @@ public static List ArgsAsBoolList(string opName, SExp args, int count)
List boolList = ArgsAsBools(opName, args).ToList();
if (boolList.Count != count)
{
- string plural = count != 1 ? "s" : "";
- throw new EvalError($"{opName} takes exactly {count} argument{plural}", args);
+ throw new EvalError($"{opName} takes exactly {count} arguments", args);
}
return boolList;
}
+ public static Tuple OpSubtract(SExp args)
+ {
+ BigInteger cost = Costs.ARITH_BASE_COST;
+
+ if (args.Nullp())
+ {
+ return MallocCost(cost, SExp.To(0));
+ }
+
+ BigInteger sign = 1;
+ BigInteger total = 0;
+ BigInteger arg_size = 0;
+
+ foreach (var pair in ArgsAsInts("-", args))
+ {
+ var r = pair.Item1;
+ var l = pair.Item2;
+
+ total += sign * r;
+ sign = -1;
+ arg_size += l;
+ cost += Costs.ARITH_COST_PER_ARG;
+ }
+
+ cost += arg_size * Costs.ARITH_COST_PER_BYTE;
+ return MallocCost(cost, SExp.To(total));
+ }
+
+
+ public static Tuple OpMultiply(SExp args)
+ {
+ BigInteger cost = Costs.MUL_BASE_COST;
+ var operands = ArgsAsInts("*", args);
+
+ try
+ {
+ var firstOperand = operands.First();
+ var v = firstOperand.Item1;
+ var vs = firstOperand.Item2;
+
+ foreach (var (r, rs) in operands.Skip(1))
+ {
+ cost += Costs.MUL_COST_PER_OP;
+ cost += (rs + vs) * Costs.MUL_LINEAR_COST_PER_BYTE;
+ cost += (rs * vs) / Costs.MUL_SQUARE_COST_PER_BYTE_DIVIDER;
+ v = v * r;
+ vs = (v); // Assuming limbs_for_int function is defined
+ }
+
+ return MallocCost(cost, SExp.To(v)); // Assuming malloc_cost and args.to functions are defined
+ }
+ catch (InvalidOperationException)
+ {
+ return MallocCost(cost, SExp.To(1)); // Assuming malloc_cost and args.to functions are defined
+ }
+ }
+
+
public static Tuple OpAdd(SExp args)
{
BigInteger total = 0;
@@ -131,9 +196,9 @@ public static Tuple OpAdd(SExp args)
return MallocCost(cost, SExp.To(total));
}
- public static (BigInteger, SExp) OpDivmod(SExp args)
+ public static Tuple OpDivmod(SExp args)
{
- BigInteger cost = Costs.DIV_BASE_COST;
+ BigInteger cost = Costs.DIVMOD_BASE_COST;
var (i0, l0) = ArgsAsIntList("divmod", args, 2)[0];
var (i1, l1) = ArgsAsIntList("divmod", args, 2)[1];
if (i1 == 0)
@@ -141,12 +206,13 @@ public static (BigInteger, SExp) OpDivmod(SExp args)
throw new EvalError("divmod with 0", SExp.To(i0));
}
- cost += (l0 + l1) * Costs.DIV_COST_PER_BYTE;
+ cost += (l0 + l1) * Costs.DIVMOD_COST_PER_BYTE;
BigInteger q = BigInteger.DivRem(i0, i1, out BigInteger r);
SExp q1 = SExp.To(q);
SExp r1 = SExp.To(r);
cost += (q1.Atom.Length + r1.Atom.Length) * Costs.MALLOC_COST_PER_BYTE;
- return (cost, SExp.To(new List { q1, r1 }));
+ var ex = SExp.To((q1, r1));
+ return MallocCost(cost, ex);
}
public static Tuple OpDiv(SExp args)
@@ -170,77 +236,115 @@ public static Tuple OpDiv(SExp args)
return MallocCost(cost, SExp.To(q));
}
- // public (BigInteger, SExp) OpGr(SExp args)
- // {
- // var (i0, l0) = ArgsAsIntList(">", args, 2);
- // BigInteger cost = Costs.GR_BASE_COST;
- // cost += (l0 + ArgsAsIntList(">", args, 2)[1].Item2) * Costs.GR_COST_PER_BYTE;
- // return (cost, i0 > ArgsAsIntList(">", args, 2)[1].Item1 ? SExp.True : SExp.False);
- // }
-//
-// public (BigInteger, SExp) OpGrBytes(dynamic args)
-// {
-// var argList = args.AsList();
-// if (argList.Count != 2)
-// {
-// throw new EvalError(">s takes exactly 2 arguments", args);
-// }
-// var a0 = argList[0];
-// var a1 = argList[1];
-// if (a0.IsPair || a1.IsPair)
-// {
-// throw new EvalError(">s on list", a0.IsPair ? a0 : a1);
-// }
-// var b0 = a0.AsAtom();
-// var b1 = a1.AsAtom();
-// BigInteger cost = Costs.GRS_BASE_COST;
-// cost += (b0.Length + b1.Length) * Costs.GRS_COST_PER_BYTE;
-// return (cost, b0.CompareTo(b1) > 0 ? args.True : args.False);
-// }
-//
-// public (BigInteger, SExp) OpPubkeyForExp(SExp args)
-// {
-// var (i0, l0) = ArgsAsIntList("pubkey_for_exp", args, 1)[0];
-// i0 %= BigInteger.Parse("0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001");
-// Exponent exponent = PrivateKey.FromBytes(i0.ToByteArray());
-// try
-// {
-// G1.Generator.ToBytes();
-// SExp r = args.To(exponent.GetG1().ToByteArray());
-// BigInteger cost = Costs.PUBKEY_BASE_COST;
-// cost += l0 * Costs.PUBKEY_COST_PER_BYTE;
-// return MallocCost(cost, r);
-// }
-// catch (Exception ex)
-// {
-// throw new EvalError($"problem in op_pubkey_for_exp: {ex}", args);
-// }
-// }
-//
-// public (BigInteger, SExp) OpPointAdd(dynamic items)
-// {
-// BigInteger cost = Costs.POINT_ADD_BASE_COST;
-// G1 p = new G1();
-//
-// foreach (var item in items.AsEnumerable())
-// {
-// if (item.IsPair)
-// {
-// throw new EvalError("point_add on list", item);
-// }
-// try
-// {
-// p += G1.FromBytes(item.AsAtom());
-// cost += Costs.POINT_ADD_COST_PER_ARG;
-// }
-// catch (Exception ex)
-// {
-// throw new EvalError($"point_add expects blob, got {item}: {ex}", items);
-// }
-// }
-// return MallocCost(cost, items.To(p.ToBytes()));
-// }
-//
+ public static Tuple OpGr(SExp args)
+ {
+ var list = ArgsAsIntList(">", args, 2).ToArray();
+ var (i0, l0) = (list[0].Item1, list[0].Item2);
+ var (i1, l1) = (list[1].Item1, list[1].Item2);
+
+ BigInteger cost = Costs.GR_BASE_COST;
+ cost += (l0 + l1) * Costs.GR_COST_PER_BYTE;
+ if (i0 > i1)
+ return Tuple.Create(cost, SExp.True);
+
+ return Tuple.Create(cost, SExp.False);
+ }
+
+
+ public static Tuple OpGrBytes(SExp args)
+ {
+ var argList = args.AsIter().ToList();
+ if (argList.Count != 2)
+ {
+ throw new EvalError(">s takes exactly 2 arguments", args);
+ }
+
+ var a0 = argList[0];
+ var a1 = argList[1];
+ if (a0.Pair != null || a1.Pair != null)
+ {
+ throw new EvalError(">s on list", a0.Pair != null ? a0 : a1);
+ }
+
+ var b0 = a0.AsAtom();
+ var b1 = a1.AsAtom();
+ BigInteger cost = Costs.GRS_BASE_COST;
+ cost += (b0.Length + b1.Length) * Costs.GRS_COST_PER_BYTE;
+
+ int comparisonResult = b0.AsSpan().SequenceCompareTo(b1.AsSpan());
+
+ return Tuple.Create(cost, comparisonResult > 0 ? SExp.True : SExp.False);
+ }
+
+ public static Tuple OpPubkeyForExp(SExp args)
+ {
+ var (i0, l0) = ArgsAsIntList("pubkey_for_exp", args, 1)[0];
+ string hexValue = "73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001";
+ BigInteger largeNumber = BigInteger.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
+
+ // Ensure i0 is positive
+ if (i0 < 0)
+ {
+ i0 += largeNumber; // Add largeNumber to make it positive
+ }
+ i0 %= largeNumber;
+
+ var keyBytes = BigIntegerToBytesWithLittleEndian(i0);
+ var sk = PrivateKey.FromBytes(keyBytes);
+ try
+ {
+ var g1 = sk.GetG1().ToBytes();
+ SExp r = SExp.To(g1);
+ BigInteger cost = Costs.PUBKEY_BASE_COST;
+ cost += l0 * Costs.PUBKEY_COST_PER_BYTE;
+ return MallocCost(cost, r);
+ }
+ catch (Exception ex)
+ {
+ throw new EvalError($"problem in op_pubkey_for_exp: {ex}", args);
+ }
+
+ static byte[] BigIntegerToBytesWithLittleEndian(BigInteger v)
+ {
+ byte[] byteArray = v.ToByteArray();
+ byteArray = byteArray.Reverse().ToArray();
+ if (byteArray.Length < 32)
+ {
+ byte[] paddedArray = new byte[32];
+ Array.Copy(byteArray, 0, paddedArray, 32 - byteArray.Length, byteArray.Length);
+ byteArray = paddedArray;
+ }
+
+ return byteArray;
+ }
+ }
+
+
+ public static Tuple OpPointAdd(dynamic items)
+ {
+ throw new Exception("Not implemented Exception");
+ // BigInteger cost = Costs.POINT_ADD_BASE_COST;
+ // var g1 = new JacobianPoint();
+ //
+ // foreach (var item in items.AsEnumerable())
+ // {
+ // if (item.IsPair)
+ // {
+ // throw new EvalError("point_add on list", item);
+ // }
+ // try
+ // {
+ // p += G1.FromBytes(item.AsAtom());
+ // cost += Costs.POINT_ADD_COST_PER_ARG;
+ // }
+ // catch (Exception ex)
+ // {
+ // throw new EvalError($"point_add expects blob, got {item}: {ex}", items);
+ // }
+ // }
+ // return MallocCost(cost, items.To(p.ToBytes()));
+ }
+
public static Tuple OpStrlen(SExp args)
{
if (args.ListLength() != 1)
@@ -275,27 +379,57 @@ public static Tuple OpSubstr(SExp args)
var s0 = a0.AsAtom();
- int i1, i2;
+ BigInteger i1 = 0, i2 = 0;
+ IEnumerable lst = ArgsAsInt32("substr", args.Rest());
+ int charsToTake = 0;
if (argCount == 2)
{
- i1 = ArgsAsInt32("substr", args.Rest()).Single();
- i2 = s0.Length;
+ //substring starting at index, take the rest of the string
+ var array = lst.ToArray();
+ i1 = array[0];
+ i2 = 0;
}
else
{
- var intArgs = ArgsAsInt32("substr", args.Rest()).ToArray();
- i1 = intArgs[0];
- i2 = intArgs[1];
+ //substring starting at index, take x amount of characters
+ var array = lst.ToArray();
+ i1 = array[0];
+ i2 = (int)array[1];
+ ;
}
- if (i2 > s0.Length || i2 < i1 || i2 < 0 || i1 < 0)
+ if (i2 > s0.Length || i2 < 0 || i1 > s0.Length || i1 < 0 || (argCount > 2 && i2 < i1))
{
throw new EvalError("invalid indices for substr", args);
}
- var s = s0.SubString(i1, i2 - i1);
- BigInteger cost = 1;
- return MallocCost(cost, SExp.To(s));
+ //much easier to work with strings
+ if (s0 is byte[] arr)
+ {
+ var startIndex = (int)(i2 - i1) + 1;
+ //if there isn't a second int to use to substring i.e. how many characters to take
+ //take them all
+ if (i2 == 0)
+ {
+ string text = Encoding.UTF8.GetString(arr);
+ var s = text.Substring((int)i1);
+ BigInteger cost = 1;
+ return Tuple.Create(cost, SExp.To(s));
+ }
+ else
+ {
+ var endIndex = (int)(i2 - i1);
+ string text = Encoding.UTF8.GetString(arr);
+ var s = text.Substring((int)i1, endIndex);
+ BigInteger cost = 1;
+ return Tuple.Create(cost, SExp.To(s));
+ }
+ }
+ else
+ {
+ BigInteger cost = 1;
+ return Tuple.Create(cost, SExp.To(s0));
+ }
}
public static Tuple OpConcat(SExp args)
@@ -319,74 +453,87 @@ public static Tuple OpConcat(SExp args)
return MallocCost(cost, SExp.To(r));
}
- // public static Tuple OpAsh(SExp args)
- // {
- // var (i0, l0) = ArgsAsIntList("ash", args, 2);
- // var (i1, l1) = ArgsAsIntList("ash", args.Rest(), 1).First();
- //
- // if (l1 > 4)
- // {
- // throw new EvalError("ash requires int32 args (with no leading zeros)", args.Rest().First());
- // }
- //
- // if (Math.Abs(i1) > 65535)
- // {
- // throw new EvalError("shift too large", args.To(i1));
- // }
- //
- // BigInteger r;
- //
- // if (i1 >= 0)
- // {
- // r = i0 << (int)i1;
- // }
- // else
- // {
- // r = i0 >> (int)-i1;
- // }
- //
- // BigInteger cost = Costs.ASHIFT_BASE_COST;
- // cost += (l0 + Casts.LimbsForInt(r)) * Costs.ASHIFT_COST_PER_BYTE;
- //
- // return MallocCost(cost, SExp.To(r));
- // }
-//
-// public (BigInteger, SExp) OpLsh(SExp args)
-// {
-// var (i0, l0) = ArgsAsIntList("lsh", args, 2);
-// var (i1, l1) = ArgsAsIntList("lsh", args.Rest(), 1).First();
-//
-// if (l1 > 4)
-// {
-// throw new EvalError("lsh requires int32 args (with no leading zeros)", args.Rest().First());
-// }
-//
-// if (Math.Abs(i1) > 65535)
-// {
-// throw new EvalError("shift too large", args.To(i1));
-// }
-//
-// // We actually want i0 to be an unsigned int
-// var a0 = args.First().AsAtom();
-// var i0Bytes = a0.Reverse().ToArray(); // Reverse bytes for little-endian representation
-// var i0 = new BigInteger(i0Bytes);
-//
-// BigInteger r;
-//
-// if (i1 >= 0)
-// {
-// r = i0 << (int)i1;
-// }
-// else
-// {
-// r = i0 >> (int)-i1;
-// }
-//
-// BigInteger cost = Costs.LSHIFT_BASE_COST;
-// cost += (l0 + Casts.LimbsForInt(r)) * Costs.LSHIFT_COST_PER_BYTE;
-//
-// return (cost, args.To(r));
-// }
+ public static Tuple OpAsh(SExp args)
+ {
+ var list = ArgsAsIntList("ash", args, 2).ToArray();
+ var i0 = list[0].Item1;
+ var l0 = list[0].Item2;
+
+ var i1 = list[1].Item1;
+ var l1 = list[1].Item2;
+
+ if (l1 > 4)
+ {
+ throw new EvalError("ash requires int32 args (with no leading zeros)", args.Rest().First());
+ }
+
+ if (i1 > 65535)
+ {
+ throw new EvalError("shift too large", SExp.To(i1));
+ }
+
+ BigInteger r;
+
+ if (i1 >= 0)
+ {
+ r = i0 << (int)i1;
+ }
+ else
+ {
+ r = i0 >> (int)-i1;
+ }
+
+ BigInteger cost = Costs.ASHIFT_BASE_COST;
+ cost += (l0 + Casts.LimbsForInt(r)) * Costs.ASHIFT_COST_PER_BYTE;
+
+ return MallocCost(cost, SExp.To(r));
+ }
+
+ public static Tuple OpLsh(SExp args)
+ {
+ var list = ArgsAsIntList("lsh", args, 2).ToArray();
+ var i0 = list[0].Item1;
+ var l0 = list[0].Item2;
+ var i1 = list[1].Item1;
+ var l1 = list[1].Item2;
+
+ if (l1 > 4)
+ {
+ throw new EvalError("lsh requires int32 args (with no leading zeros)", args.Rest().First());
+ }
+
+ if (i1 > 65535)
+ {
+ throw new EvalError("shift too large", SExp.To(i1));
+ }
+
+ // We actually want i0 to be an unsigned int
+ byte[] a0 = args.First().AsAtom();
+ var i0Bytes = a0;
+ if (BitConverter.IsLittleEndian)
+ {
+ // Reverse the byte array for big-endian interpretation
+ Array.Reverse(i0Bytes);
+ }
+
+ i0 = new BigInteger(i0Bytes);
+
+ BigInteger r = 0;
+
+ if (i1 >= 0)
+ {
+ r = i0 << (int)i1;
+ }
+ else
+ {
+ r = (int)i0 >> (int)-i1;
+ }
+
+ BigInteger cost = Costs.LSHIFT_BASE_COST;
+ cost += (l0 + Casts.LimbsForInt(r)) * Costs.LSHIFT_COST_PER_BYTE;
+
+ return MallocCost(cost, SExp.To(r));
+ }
public static Tuple BinopReduction(string opName, BigInteger initialValue, SExp args,
Func opF)
@@ -427,39 +574,65 @@ public static Tuple OpLogxor(SExp args)
return BinopReduction("logxor", 0, args, Binop);
}
- // public static Tuple OpLognot(SExp args)
- // {
- // var (i0, l0) = ArgsAsIntList("lognot", args, 1);
- // BigInteger result = ~i0;
- // int cost = Costs.LOGNOT_BASE_COST + l0 * Costs.LOGNOT_COST_PER_BYTE;
- // return MallocCost(cost, SExp.To(result));
- // }
-
- // public static Tuple OpNot(dynamic args)
- // {
- // List boolList = ArgsAsBoolList("not", args, 1);
- // bool i0 = boolList[0];
- // bool result = !i0;
- // int cost = Costs.BOOL_BASE_COST;
- // return MallocCost(cost, args.To(result ? args.True : args.False));
- // }
-//
-// public (int, SExp) OpAny(dynamic args)
-// {
-// List boolList = ArgsAsBoolList("any", args, 1);
-// int cost = Costs.BOOL_BASE_COST + boolList.Count * Costs.BOOL_COST_PER_ARG;
-// bool result = boolList.Any(v => v);
-// return (cost, args.To(result ? args.True : args.False));
-// }
-//
- // public (int, SExp) OpAll(dynamic args)
- // {
- // List boolList = ArgsAsBoolList("all", args, 1);
- // int cost = Costs.BOOL_BASE_COST + boolList.Count * Costs.BOOL_COST_PER_ARG;
- // bool result = boolList.All(v => v);
- // return (cost, args.To(result ? args.True : args.False));
- // }
+ public static Tuple OpAny(SExp args)
+ {
+ var items = ArgsAsBools("any", args).ToList();
+ BigInteger cost = Costs.BOOL_BASE_COST + items.Count * Costs.BOOL_COST_PER_ARG;
+ SExp r = SExp.False;
+
+ foreach (var v in items)
+ {
+ var atom = v.AsAtom();
+ if (atom != null && atom.Length > 0)
+ {
+ r = SExp.True;
+ break;
+ }
+ }
+
+ return Tuple.Create(cost, r);
+ }
+
+
+ public static Tuple OpAll(SExp args)
+ {
+ var items = ArgsAsBools("all", args).ToArray();
+ BigInteger cost = Costs.BOOL_BASE_COST + items.Length * Costs.BOOL_COST_PER_ARG;
+ var r = SExp.True;
+
+ foreach (var v in items)
+ {
+ var atom = v.AsAtom();
+ if (atom != null && atom.Length == 0 || atom is null)
+ {
+ r = SExp.False;
+ break;
+ }
+ }
+ return Tuple.Create(cost, r);
+ }
+
+ public static Tuple OpLogNot(dynamic args)
+ {
+ var list = ArgsAsIntList("lognot", args, 1);
+ BigInteger cost = Costs.LOGNOT_BASE_COST + list[0].Item2 * Costs.LOGNOT_COST_PER_BYTE;
+ BigInteger s = list[0].Item1;
+ var inverted = -s - 1;
+ return MallocCost(cost, SExp.To(inverted));
+ }
+
+ public static Tuple OpNot(dynamic args)
+ {
+ var boolList = ArgsAsBoolList("not", args, 1);
+ SExp i0 = boolList[0];
+ int cost = Costs.BOOL_BASE_COST;
+
+ if (i0.AsAtom() is null || i0.AsAtom().SequenceEqual(Array.Empty()))
+ return MallocCost(cost, SExp.True);
+
+ return MallocCost(cost, SExp.False);
+ }
public static Tuple OpSoftfork(SExp args)
{
diff --git a/CLVMDotNet/src/CLVM/OpUtils.cs b/CLVMDotNet/src/CLVM/OpUtils.cs
deleted file mode 100644
index 5ce9b62..0000000
--- a/CLVMDotNet/src/CLVM/OpUtils.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Reflection;
-
-namespace CLVMDotNet.CLVM
-{
-
- public static class OpUtils
- {
- public static Dictionary> OperatorsForDict(
- Dictionary keywordToAtom,
- Dictionary> opDict,
- Dictionary opNameLookup = null)
- {
- Dictionary> result = new Dictionary>();
-
- foreach (string op in keywordToAtom.Keys)
- {
- string opName = opNameLookup != null && opNameLookup.ContainsKey(op)
- ? opNameLookup[op]
- : "op_" + op;
-
- if (opDict.TryGetValue(opName, out Func