diff --git a/.gitignore b/.gitignore index 3398d4b4..acc5c501 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,6 @@ launchSettings.json /dist # Secret test files -test_SessionIdentity.json \ No newline at end of file +test_SessionIdentity.json + +.mono/ \ No newline at end of file diff --git a/ICP.sln b/ICP.sln index 4a448821..6b100093 100644 --- a/ICP.sln +++ b/ICP.sln @@ -47,7 +47,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdjCase.ICP.WebSockets", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.WebSockets", "samples\Sample.WebSockets\Sample.WebSockets.csproj", "{B4F9E828-BC3D-4EFF-9E21-53DD82928AB0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSockets.Tests", "test\WebSockets.Tests\WebSockets.Tests.csproj", "{3015AFBC-B866-459F-B25C-4BEA00C2A91E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSockets.Tests", "test\WebSockets.Tests\WebSockets.Tests.csproj", "{3015AFBC-B866-459F-B25C-4BEA00C2A91E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLS.Tests", "test\BLS.Tests\BLS.Tests.csproj", "{213F30BA-D147-4291-93A3-13A8A006126D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -111,6 +113,10 @@ Global {3015AFBC-B866-459F-B25C-4BEA00C2A91E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3015AFBC-B866-459F-B25C-4BEA00C2A91E}.Release|Any CPU.ActiveCfg = Release|Any CPU {3015AFBC-B866-459F-B25C-4BEA00C2A91E}.Release|Any CPU.Build.0 = Release|Any CPU + {213F30BA-D147-4291-93A3-13A8A006126D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {213F30BA-D147-4291-93A3-13A8A006126D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {213F30BA-D147-4291-93A3-13A8A006126D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {213F30BA-D147-4291-93A3-13A8A006126D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -126,6 +132,7 @@ Global {94023B5A-0650-4E67-A685-D1C979DA717F} = {9A64FA9E-AC6C-4C1F-B648-E9E64F172EA8} {B4F9E828-BC3D-4EFF-9E21-53DD82928AB0} = {7FADA9D9-5FDA-4CFB-8A53-A578A61FBBA9} {3015AFBC-B866-459F-B25C-4BEA00C2A91E} = {F71B8320-C279-4A79-A8D4-4039DB39D522} + {213F30BA-D147-4291-93A3-13A8A006126D} = {F71B8320-C279-4A79-A8D4-4039DB39D522} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3103E11A-E792-49EE-98C5-B2F3709DB088} diff --git a/README.md b/README.md index 059164df..4569d827 100644 --- a/README.md +++ b/README.md @@ -141,4 +141,4 @@ public enum MyVariantTag - [IC Http Interface Spec](https://smartcontracts.org/docs/current/references/ic-interface-spec) - [Candid Spec](https://github.com/dfinity/candid/blob/master/spec/Candid.md) - [Candid Decoder](https://fxa77-fiaaa-aaaae-aaana-cai.raw.ic0.app/explain) -- [Candid UI Tester](https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app) +- [Candid UI Tester](https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app) \ No newline at end of file diff --git a/UnityAssets/WebGlBlsCryptography.cs b/UnityAssets/WebGlBlsCryptography.cs index fd45f157..9a371626 100644 --- a/UnityAssets/WebGlBlsCryptography.cs +++ b/UnityAssets/WebGlBlsCryptography.cs @@ -9,19 +9,46 @@ using UnityEngine; using UnityEngine.Networking; -#if UNITY_WEBGL +#if UNITY_WEBGL && !UNITY_EDITOR public class WebGlBlsCryptography : IBlsCryptography { public bool VerifySignature(byte[] publicKey, byte[] messageHash, byte[] signature) { - return BrowserBlsLib.VerifySignature(publicKey, messageHash, signature); + string publicKeyHex = ToHexString(publicKey); + string messageHashHex = ToHexString(messageHash); + string signatureHex = ToHexString(signature); + return BrowserBlsLib.VerifySignature(publicKeyHex, messageHashHex, signatureHex); + } + + public static string ToHexString(byte[] bytes) + { + char[] stringValue = new char[bytes.Length * 2]; + int i = 0; + foreach (byte b in bytes) + { + int charIndex = i++ * 2; + int quotient = Math.DivRem(b, 16, out int remainder); + stringValue[charIndex] = GetChar(quotient); + stringValue[charIndex + 1] = GetChar(remainder); + } + + return new string(stringValue); // returns: "48656C6C6F20776F726C64" for "Hello world" + + } + private static char GetChar(int value) + { + if (value < 10) + { + return (char)(value + 48); // 0->9 + } + return (char)(value + 65 - 10); // A->F ASCII } } internal static class BrowserBlsLib { [DllImport("__Internal")] - public static extern bool VerifySignature(byte[] publicKey, byte[] messageHash, byte[] signature); + public static extern bool VerifySignature(string publicKeyHex, string messageHashHex, string signatureHex); } #endif diff --git a/samples/Sample.CLI/Program.cs b/samples/Sample.CLI/Program.cs index 0438db48..1f4989ba 100644 --- a/samples/Sample.CLI/Program.cs +++ b/samples/Sample.CLI/Program.cs @@ -15,6 +15,10 @@ using EdjCase.ICP.Agent.Standards.AssetCanister; using System.Text; using EdjCase.ICP.Agent.Standards.AssetCanister.Models; +using EdjCase.ICP.BLS; +using EdjCase.ICP.Candid.Utilities; +using EdjCase.ICP.BLS.Models; +using System.Diagnostics; public class Program { @@ -182,7 +186,7 @@ string filePath try { contentStream = File.OpenRead(filePath); - + await client.UploadAssetChunkedAsync( key: key, contentType: contentType, diff --git a/src/Agent/API.xml b/src/Agent/API.xml index 8f3cf9d3..d10f6da1 100644 --- a/src/Agent/API.xml +++ b/src/Agent/API.xml @@ -4,45 +4,6 @@ EdjCase.ICP.Agent - - - An `IAgent` implementation using HTTP to make requests to the IC - - - - - The identity that will be used on each request unless overriden - This identity can be anonymous - - - - Optional. Identity to use for each request. If unspecified, will use anonymous identity - Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation - Optional. Sets the http client to use, otherwise will use the default http client - - - Optional. Identity to use for each request. If unspecified, will use anonymous identity - Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation - Url to the boundry node to connect to. Defaults to `https://ic0.app/` - - - - - - - - - - - - - - - - - - - The default http client to use with the built in `HttpClient` @@ -111,6 +72,45 @@ + + + An `IAgent` implementation using HTTP to make requests to the IC + + + + + The identity that will be used on each request unless overriden + This identity can be anonymous + + + + Optional. Identity to use for each request. If unspecified, will use anonymous identity + Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation + Optional. Sets the http client to use, otherwise will use the default http client + + + Optional. Identity to use for each request. If unspecified, will use anonymous identity + Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation + Url to the boundry node to connect to. Defaults to `https://ic0.app/` + + + + + + + + + + + + + + + + + + + An agent is used to communicate with the Internet Computer with certain protocols that diff --git a/src/Agent/Agents/HttpAgent.cs b/src/Agent/Agents/HttpAgent.cs index d406f582..a49d066c 100644 --- a/src/Agent/Agents/HttpAgent.cs +++ b/src/Agent/Agents/HttpAgent.cs @@ -48,7 +48,7 @@ public HttpAgent( { this.Identity = identity; this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); - this.bls = bls ?? new WasmBlsCryptography(); + this.bls = bls ?? new DefaultBlsCryptograhy(); } /// Optional. Identity to use for each request. If unspecified, will use anonymous identity @@ -65,7 +65,7 @@ public HttpAgent( { BaseAddress = httpBoundryNodeUrl ?? new Uri("https://ic0.app/") }); - this.bls = bls ?? new WasmBlsCryptography(); + this.bls = bls ?? new DefaultBlsCryptograhy(); } diff --git a/src/Agent/README.md b/src/Agent/README.md index 25637680..1e16ed4f 100644 --- a/src/Agent/README.md +++ b/src/Agent/README.md @@ -40,7 +40,7 @@ IAgent agent = new HttpAgent(); // Create Candid arg to send in request ulong proposalId = 1234; CandidArg arg = CandidArg.FromCandid( - CandidTypedValue.FromObject(proposalId) // Conversion can be C# or custom types + CandidTypedValue.FromObject(proposalId) // Conversion can be C# or custom types ); // Make request to IC @@ -89,19 +89,19 @@ string name = await client.Name(); // Get the balance of a specific account Account account = new Account { - Id = Principal.FromText("") + Id = Principal.FromText("") }; UnboundedUInt balance = await client.BalanceOf(account); // Transfer tokens from one account to another TransferArgs transferArgs = new TransferArgs { - To = new Account - { - Id = Principal.FromText("") - }, - Amount = 1, - Memo = "" + To = new Account + { + Id = Principal.FromText("") + }, + Amount = 1, + Memo = "" }; TransferResult transferResult = await client.Transfer(transferArgs); ``` @@ -172,18 +172,7 @@ var delegatedIdentity = new DelegationIdentity(innerIdentity, chain); Due to how WebGL works by converting C# to JS/WASM using IL2CPP there are a few additional steps to avoid incompatibilities. - UnityHttpClient - The .NET `HttpClient` does not work in many cases, so `UnityHttpClient` is added via Unity C# script. - ```cs - var client = new UnityHttpClient(); - var agent = new HttpAgent(client); - ``` -- WebGlBlsCryptography - The BLS signature verification relies on a 3rd party library and due to that library not being directly compatible with the WebGL builds, `WebGlBlsCryptography` needs to be used instead of the default `WasmBlsCryptography` class. - - ```cs - var bls = new WebGlBlsCrytography(); - var agent = new HttpAgent(client, bls: bls); - ``` - In addition, the `noble-curves.js` file located [HERE](`https://github.com/paulmillr/noble-curves/releases/tag/1.2.0`) needs to be included in the Unity project and referenced by the HTML page - ```html -