Skip to content

Commit

Permalink
Fixing value tuples (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekctek authored Apr 27, 2023
1 parent 9b9fc3f commit 1a62157
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 31 deletions.
24 changes: 24 additions & 0 deletions src/Candid/Mapping/IResolvableTypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ private static IResolvableTypeInfo BuildTypeInfo(Type objType)
{
return BuildOpt(objType);
}
if (objType.Name.StartsWith("ValueTuple"))
{
return BuildTuple(objType, objType.GenericTypeArguments);
}
if (genericTypeDefinition == typeof(List<>))
{
Type innerType = objType.GenericTypeArguments[0];
Expand Down Expand Up @@ -462,6 +466,26 @@ private static IResolvableTypeInfo BuildVariant(Type objType, VariantAttribute a
});
}

private static IResolvableTypeInfo BuildTuple(Type objType, Type[] innerTypes)
{

return new ComplexTypeInfo(objType, innerTypes.ToList(), (resolvedMappings) =>
{
List<(Type, CandidType)> tupleTypes = innerTypes
.Select(p => (p, resolvedMappings[p]))
.ToList();
Dictionary<CandidTag, CandidType> fieldTypes = tupleTypes
.Select((t, i) => (Index: i, Type: t))
.ToDictionary(
p => CandidTag.FromId((uint)p.Index),
p => p.Type.Item2
);
CandidRecordType type = new CandidRecordType(fieldTypes);

return (new TupleMapper(objType, tupleTypes), type);
});
}

private static IResolvableTypeInfo BuildRecord(Type objType)
{
List<PropertyInfo> properties = objType
Expand Down
81 changes: 81 additions & 0 deletions src/Candid/Mapping/Mappers/TupleMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using EdjCase.ICP.Candid.Models.Types;
using EdjCase.ICP.Candid.Models.Values;
using EdjCase.ICP.Candid.Models;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Runtime.CompilerServices;

namespace EdjCase.ICP.Candid.Mapping.Mappers
{

internal class TupleMapper : ICandidValueMapper
{
public CandidType CandidType { get; }

public Type Type { get; }
public List<Type> Types { get; }

public TupleMapper(Type type, List<(Type, CandidType)> tupleTypes)
{
Dictionary<CandidTag, CandidType> fields = tupleTypes
.Select((t, i) => (Index: i, Type: t.Item2))
.ToDictionary(t => CandidTag.FromId((uint)t.Index), t => t.Type);
this.Type = type;
this.CandidType = new CandidRecordType(fields);
this.Types = tupleTypes.Select(t => t.Item1).ToList();
}

public object Map(CandidValue value, CandidConverter converter)
{
CandidRecord record = value.AsRecord();
object obj = Activator.CreateInstance(this.Type);
static void SetValue(object tuple, int itemIndex, object? itemValue)
{
if (itemIndex >= 7)
{
// Index 7+ is located in the "Rest" which is a nested tuple
object restTuple = tuple.GetType().GetField("Rest").GetValue(tuple);
SetValue(restTuple, itemIndex - 7, itemValue);
}
else
{
tuple.GetType().GetField("Item" + (itemIndex + 1)).SetValue(tuple, itemValue);
}
}
uint i = 0;
foreach (Type fieldType in this.Types)
{
CandidTag fieldId = CandidTag.FromId(i);
if (!record.Fields.TryGetValue(fieldId, out CandidValue fieldCandidValue))
{
throw new Exception($"Could not map candid record to type '{this.Type}'. Record is missing field '{fieldId}'");
}
object? fieldValue = converter.ToObject(fieldType, fieldCandidValue);
SetValue(obj, (int)i, fieldValue);
i++;
}
return obj;
}

public CandidValue Map(object value, CandidConverter converter)
{
ITuple tuple = (ITuple)value;
Dictionary<CandidTag, CandidValue> fields = new();
int i = 0;
foreach (Type type in this.Types)
{
object propValue = tuple[i];
CandidValue v = converter.FromObject(propValue);
fields.Add(CandidTag.FromId((uint)i), v);
}
return new CandidRecord(fields);
}

public CandidType? GetMappedCandidType(Type type)
{
return this.CandidType;
}
}
}
4 changes: 3 additions & 1 deletion src/Candid/Utilities/ByteUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ public static string ToHexString(this Span<byte> bytes)

public static string ToHexString(this ReadOnlySpan<byte> bytes)
{
Span<char> stringValue = stackalloc char[bytes.Length * 2];
Span<char> stringValue = bytes.Length > 1_000
? new char[bytes.Length * 2]
: stackalloc char[bytes.Length * 2];
int i = 0;
foreach (byte b in bytes)
{
Expand Down
47 changes: 17 additions & 30 deletions src/EdjCase.Cryptography.BLS/IcpBlsUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class IcpBlsUtil

private static object intializeLock = new object();
private static bool isInitialized = false;
private static DelegatesCache cache;
private static DelegatesCache? cache;

/// <summary>
/// Verifies a BLS signature (ICP flavor only)
Expand Down Expand Up @@ -53,7 +53,7 @@ byte[] signature
EnsureInitialized();

var blsPublicKey = default(Interop.PublicKey);
ulong publicKeyBytesRead = cache.publicKeyDeserialize(ref blsPublicKey, publicKey, (ulong)publicKey!.LongLength);
ulong publicKeyBytesRead = cache!.publicKeyDeserialize(ref blsPublicKey, publicKey, (ulong)publicKey!.LongLength);

if (publicKeyBytesRead != (ulong)publicKey.Length)
{
Expand Down Expand Up @@ -166,37 +166,24 @@ public static DelegatesCache Create()
Delegates.SignatureDeserialize signatureDeserialize;
Delegates.Verify verify;
Delegates.PublicKeySetHexStr publicKeySetHexStr;
if (true)
{
string libraryName = "bls384_256";
const string libraryName = "bls384_256";

IntPtr libraryHandle = NativeInterop.LoadNativeLibrary(libraryName);
T Get<T>(string functionName)
{
IntPtr blsInitPtr = NativeInterop.GetFunctionPointer(libraryHandle, functionName);
return Marshal.GetDelegateForFunctionPointer<T>(blsInitPtr);
}
init = Get<Delegates.Init>("blsInit");
setEthSerialization = Get<Delegates.SetEthSerialization>("blsSetETHserialization");
setMapToMode = Get<Delegates.SetMapToMode>("blsSetMapToMode");
setGeneratorOfPublicKey = Get<Delegates.SetGeneratorOfPublicKey>("blsSetGeneratorOfPublicKey");
mclBnG1SetDst = Get<Delegates.MclBnG1SetDst>("mclBnG1_setDst");
publicKeyDeserialize = Get<Delegates.PublicKeyDeserialize>("blsPublicKeyDeserialize");
signatureDeserialize = Get<Delegates.SignatureDeserialize>("blsSignatureDeserialize");
verify = Get<Delegates.Verify>("blsVerify");
publicKeySetHexStr = Get<Delegates.PublicKeySetHexStr>("blsPublicKeySetHexStr");
}
else
IntPtr libraryHandle = NativeInterop.LoadNativeLibrary(libraryName);
T Get<T>(string functionName)
{
init = Interop.blsInit;
setEthSerialization = Interop.blsSetETHserialization;
setMapToMode = Interop.blsSetMapToMode;
setGeneratorOfPublicKey = Interop.blsSetGeneratorOfPublicKey;
mclBnG1SetDst = Interop.mclBnG1_setDst;
publicKeyDeserialize = Interop.blsPublicKeyDeserialize;
signatureDeserialize = Interop.blsSignatureDeserialize;
verify = Interop.blsVerify;
IntPtr blsInitPtr = NativeInterop.GetFunctionPointer(libraryHandle, functionName);
return Marshal.GetDelegateForFunctionPointer<T>(blsInitPtr);
}
init = Get<Delegates.Init>("blsInit");
setEthSerialization = Get<Delegates.SetEthSerialization>("blsSetETHserialization");
setMapToMode = Get<Delegates.SetMapToMode>("blsSetMapToMode");
setGeneratorOfPublicKey = Get<Delegates.SetGeneratorOfPublicKey>("blsSetGeneratorOfPublicKey");
mclBnG1SetDst = Get<Delegates.MclBnG1SetDst>("mclBnG1_setDst");
publicKeyDeserialize = Get<Delegates.PublicKeyDeserialize>("blsPublicKeyDeserialize");
signatureDeserialize = Get<Delegates.SignatureDeserialize>("blsSignatureDeserialize");
verify = Get<Delegates.Verify>("blsVerify");
publicKeySetHexStr = Get<Delegates.PublicKeySetHexStr>("blsPublicKeySetHexStr");

return new DelegatesCache(
init,
setEthSerialization,
Expand Down

0 comments on commit 1a62157

Please sign in to comment.