From a3ab0f8c10a18918dc95f30fe19f4dd38f69f388 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Tue, 19 Apr 2022 20:10:29 +0800 Subject: [PATCH 1/3] [wip] add dotnet target --- target-dotnet/.gitignore | 454 +++++++++++++++++++ target-dotnet/MaskWalletCore.sln | 28 ++ target-dotnet/rust/.gitignore | 1 + target-dotnet/rust/Cargo.toml | 17 + target-dotnet/rust/src/lib.rs | 34 ++ target-dotnet/src/MaskWalletCore.csproj | 54 +++ target-dotnet/src/MaskWalletCoreException.cs | 32 ++ target-dotnet/src/Native.cs | 32 ++ target-dotnet/src/PersonaKey.cs | 80 ++++ target-dotnet/src/RustByteSlice.cs | 19 + target-dotnet/src/WalletKey.cs | 39 ++ target-dotnet/test/MaskWalletCoreTest.csproj | 20 + target-dotnet/test/PersonaKeyTest.cs | 19 + 13 files changed, 829 insertions(+) create mode 100644 target-dotnet/.gitignore create mode 100644 target-dotnet/MaskWalletCore.sln create mode 100644 target-dotnet/rust/.gitignore create mode 100644 target-dotnet/rust/Cargo.toml create mode 100644 target-dotnet/rust/src/lib.rs create mode 100644 target-dotnet/src/MaskWalletCore.csproj create mode 100644 target-dotnet/src/MaskWalletCoreException.cs create mode 100644 target-dotnet/src/Native.cs create mode 100644 target-dotnet/src/PersonaKey.cs create mode 100644 target-dotnet/src/RustByteSlice.cs create mode 100644 target-dotnet/src/WalletKey.cs create mode 100644 target-dotnet/test/MaskWalletCoreTest.csproj create mode 100644 target-dotnet/test/PersonaKeyTest.cs diff --git a/target-dotnet/.gitignore b/target-dotnet/.gitignore new file mode 100644 index 0000000..a72f3dd --- /dev/null +++ b/target-dotnet/.gitignore @@ -0,0 +1,454 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/target-dotnet/MaskWalletCore.sln b/target-dotnet/MaskWalletCore.sln new file mode 100644 index 0000000..2cde645 --- /dev/null +++ b/target-dotnet/MaskWalletCore.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaskWalletCore", "src\MaskWalletCore.csproj", "{B5FD990D-F2A5-4518-9E0C-1E78A2B3301B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaskWalletCoreTest", "test\MaskWalletCoreTest.csproj", "{9BDFB79F-2B4B-4950-9C92-B6AEC5EB4D10}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B5FD990D-F2A5-4518-9E0C-1E78A2B3301B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5FD990D-F2A5-4518-9E0C-1E78A2B3301B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5FD990D-F2A5-4518-9E0C-1E78A2B3301B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5FD990D-F2A5-4518-9E0C-1E78A2B3301B}.Release|Any CPU.Build.0 = Release|Any CPU + {9BDFB79F-2B4B-4950-9C92-B6AEC5EB4D10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BDFB79F-2B4B-4950-9C92-B6AEC5EB4D10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BDFB79F-2B4B-4950-9C92-B6AEC5EB4D10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BDFB79F-2B4B-4950-9C92-B6AEC5EB4D10}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/target-dotnet/rust/.gitignore b/target-dotnet/rust/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/target-dotnet/rust/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/target-dotnet/rust/Cargo.toml b/target-dotnet/rust/Cargo.toml new file mode 100644 index 0000000..b3156c1 --- /dev/null +++ b/target-dotnet/rust/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "maskwalletcore-dotnet" +version = "0.1.0" +authors = ["Tlaster "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "maskwalletcore_dotnet" +crate-type = ["cdylib"] + +[dependencies] +interface = { path = "../../interface" } +libc = "0.2.124" + +[workspace] diff --git a/target-dotnet/rust/src/lib.rs b/target-dotnet/rust/src/lib.rs new file mode 100644 index 0000000..24fdcbd --- /dev/null +++ b/target-dotnet/rust/src/lib.rs @@ -0,0 +1,34 @@ +#[allow(unused_imports)] +use std::slice; + +#[repr(C)] +pub struct RustByteSlice { + pub bytes: *const u8, + pub len: usize, +} + +/// # Safety +/// +/// The caller should provide a pointer that points to a valid bytes array of size eqqual to `len` +#[no_mangle] +pub unsafe extern "C" fn rust_request(bytes: *const u8, len: usize) -> RustByteSlice { + let byte_slice = slice::from_raw_parts(bytes, len as usize); + + let response_bytes = interface::call_api(byte_slice); + let bytes_ptr = response_bytes.as_ptr(); + let bytes_len = response_bytes.len(); + std::mem::forget(response_bytes); + RustByteSlice { + bytes: bytes_ptr, + len: bytes_len, + } +} + +/// # Safety +/// +/// The caller should provide a `RustByteSlice` struct with a valid bytes array and its size +#[no_mangle] +pub unsafe extern "C" fn rust_free(input: RustByteSlice) { + let slice = slice::from_raw_parts_mut(input.bytes as *mut u8, input.len as usize); + let _: Box<[u8]> = Box::from_raw(slice); +} diff --git a/target-dotnet/src/MaskWalletCore.csproj b/target-dotnet/src/MaskWalletCore.csproj new file mode 100644 index 0000000..2633f89 --- /dev/null +++ b/target-dotnet/src/MaskWalletCore.csproj @@ -0,0 +1,54 @@ + + + + net6.0 + enable + enable + Dimension.MaskWalletCore + MaskWalletCore + Dimension + https://github.com/DimensionDev/MaskWalletCore + https://raw.githubusercontent.com/DimensionDev/MaskWalletCore/master/LICENSE + + + + + + + + + + + + + + + + + + ../rust + + + + + + + + + + PreserveNewest + .\lib + + + + + + + + PreserveNewest + .\lib + + + + + diff --git a/target-dotnet/src/MaskWalletCoreException.cs b/target-dotnet/src/MaskWalletCoreException.cs new file mode 100644 index 0000000..4491a93 --- /dev/null +++ b/target-dotnet/src/MaskWalletCoreException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Dimension.MaskWalletCore; + +[Serializable] +public class MaskWalletCoreException : Exception +{ + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // + + public MaskWalletCoreException() + { + } + + public MaskWalletCoreException(string message) : base(message) + { + } + + public MaskWalletCoreException(string message, Exception inner) : base(message, inner) + { + } + + protected MaskWalletCoreException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/target-dotnet/src/Native.cs b/target-dotnet/src/Native.cs new file mode 100644 index 0000000..838d1e8 --- /dev/null +++ b/target-dotnet/src/Native.cs @@ -0,0 +1,32 @@ +using ProtoBuf; + +namespace Dimension.MaskWalletCore; + +using System.Runtime.InteropServices; + +internal class Native +{ + public static Api.MWResponse Call(Api.MWRequest request) + { + using var stream = new MemoryStream(); + Serializer.Serialize(stream, request); + stream.Position = 0L; + var data = stream.ToArray(); + using var slice = rust_request(data, Convert.ToUInt64(data.Length)); + var buffer = new byte[slice.len]; + Marshal.Copy(slice.bytes, buffer, 0, buffer.Length); + using var responseStream = new MemoryStream(buffer); + responseStream.Position = 0L; + var result = Serializer.Deserialize(responseStream); + if (result.Error != null) + { + throw new MaskWalletCoreException(result.Error.errorMsg); + } + return result; + } + + private const string LibName = "maskwalletcore_dotnet"; + + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] + private static extern RustByteSlice rust_request([In] byte[] data, ulong size); +} \ No newline at end of file diff --git a/target-dotnet/src/PersonaKey.cs b/target-dotnet/src/PersonaKey.cs new file mode 100644 index 0000000..e743d42 --- /dev/null +++ b/target-dotnet/src/PersonaKey.cs @@ -0,0 +1,80 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public enum CurveType +{ + Secp256k1, + Ed25519, +} + +internal static class CurveTypeExt +{ + public static PersonaGenerationParam.Curve ToCurve(this CurveType type) + { + return type switch + { + CurveType.Secp256k1 => PersonaGenerationParam.Curve.Secp256k1, + CurveType.Ed25519 => PersonaGenerationParam.Curve.Ed25519, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } +} + +public record EncryptionOption(EncryptionOption.EncVersion Version) +{ + public enum EncVersion + { + V37, + V38, + } + public EncryptOption ToEncryptOption() + { + return new EncryptOption + { + version = this.Version.ToVersion(), + }; + } +} + + +internal static class EncVersionExt +{ + public static EncryptOption.Version ToVersion(this EncryptionOption.EncVersion version) + { + return version switch + { + EncryptionOption.EncVersion.V37 => EncryptOption.Version.V37, + EncryptionOption.EncVersion.V38 => EncryptOption.Version.V38, + _ => throw new ArgumentOutOfRangeException(nameof(version), version, null) + }; + } +} + +public class PersonaKey +{ + private readonly PersonaGenerationResp _resp; + + private PersonaKey(PersonaGenerationResp resp) + { + _resp = resp; + } + + public static PersonaKey Create(string mnemonic, string password, string path, CurveType curveType, EncryptionOption option) + { + var resp = Native.Call(new MWRequest + { + ParamGeneratePersona = new PersonaGenerationParam + { + Mnemonic = mnemonic, + Password = password, + Path = path, + curve = curveType.ToCurve(), + Option = option.ToEncryptOption(), + } + }).RespGeneratePersona; + return new PersonaKey(resp); + } + + public string Identifier => _resp.Identifier; +} \ No newline at end of file diff --git a/target-dotnet/src/RustByteSlice.cs b/target-dotnet/src/RustByteSlice.cs new file mode 100644 index 0000000..f629b63 --- /dev/null +++ b/target-dotnet/src/RustByteSlice.cs @@ -0,0 +1,19 @@ +using System.Runtime.InteropServices; + +namespace Dimension.MaskWalletCore; + +[StructLayout(LayoutKind.Sequential)] +internal struct RustByteSlice : IDisposable +{ + public IntPtr bytes; + public ulong len; + + public void Dispose() + { + rust_free(this); + } + + private const string LibName = "maskwalletcore_dotnet"; + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] + public static extern void rust_free(RustByteSlice data); +} diff --git a/target-dotnet/src/WalletKey.cs b/target-dotnet/src/WalletKey.cs new file mode 100644 index 0000000..4766b22 --- /dev/null +++ b/target-dotnet/src/WalletKey.cs @@ -0,0 +1,39 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public class WalletKey +{ + public static string GenerateMnemonic() + { + return Native.Call(new MWRequest + { + ParamGenerateMnemonic = new GenerateMnemonicParam() + }).RespGenerateMnemonic.Mnemonic; + } +} + +public record JsonWebKey( + string? kty, + string? kid, + string? use, + List? key_ops, + string? alg, + Boolean? ext, + string? crv, + string? x, + string? y, + string? d, + string? n, + string? e, + string? p, + string? q, + string? dp, + string? dq, + string? qi, + List? oth, + string? k +) +{ + public record RsaOtherPrimesInfo(string r, string d, string t); +} \ No newline at end of file diff --git a/target-dotnet/test/MaskWalletCoreTest.csproj b/target-dotnet/test/MaskWalletCoreTest.csproj new file mode 100644 index 0000000..6717c0c --- /dev/null +++ b/target-dotnet/test/MaskWalletCoreTest.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + false + Dimension.MaskWalletCore + + + + + + + + + + + + + diff --git a/target-dotnet/test/PersonaKeyTest.cs b/target-dotnet/test/PersonaKeyTest.cs new file mode 100644 index 0000000..118d209 --- /dev/null +++ b/target-dotnet/test/PersonaKeyTest.cs @@ -0,0 +1,19 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dimension.MaskWalletCore; + +[TestClass] +public class PersonaKeyTest +{ + [TestMethod] + public void TestCreate() + { + const string password = "123456"; + var mnemonic = WalletKey.GenerateMnemonic(); + const string path = "m/44'/60'/0'/0/0"; + const CurveType curveType = CurveType.Secp256k1; + var option = new EncryptionOption(EncryptionOption.EncVersion.V38); + var key = PersonaKey.Create(mnemonic, password, path, curveType, option); + Assert.IsTrue(!string.IsNullOrEmpty(key.Identifier)); + } +} \ No newline at end of file From 1bd2813da0ac8a41e1057eccf46ab1e4cbea4f71 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 21 Apr 2022 17:58:29 +0800 Subject: [PATCH 2/3] complete dotnet target --- target-dotnet/src/CoinType.cs | 24 ++ target-dotnet/src/CurveType.cs | 22 ++ target-dotnet/src/EncryptionOption.cs | 33 +++ target-dotnet/src/IWalletSignInput.cs | 37 +++ target-dotnet/src/IWalletValidate.cs | 71 ++++++ target-dotnet/src/ImportExportType.cs | 34 +++ target-dotnet/src/JsonWebKey.cs | 52 ++++ target-dotnet/src/MaskWalletCore.csproj | 92 +++---- target-dotnet/src/Native.cs | 18 +- target-dotnet/src/PersonaKey.cs | 64 +---- target-dotnet/src/RustByteSlice.cs | 12 +- target-dotnet/src/SignResult.cs | 3 + target-dotnet/src/WalletAccount.cs | 19 ++ target-dotnet/src/WalletKey.cs | 252 +++++++++++++++++-- target-dotnet/src/WalletKeyType.cs | 22 ++ target-dotnet/test/MaskWalletCoreTest.cs | 42 ++++ target-dotnet/test/MaskWalletCoreTest.csproj | 30 +-- target-dotnet/test/PersonaKeyTest.cs | 19 -- 18 files changed, 676 insertions(+), 170 deletions(-) create mode 100644 target-dotnet/src/CoinType.cs create mode 100644 target-dotnet/src/CurveType.cs create mode 100644 target-dotnet/src/EncryptionOption.cs create mode 100644 target-dotnet/src/IWalletSignInput.cs create mode 100644 target-dotnet/src/IWalletValidate.cs create mode 100644 target-dotnet/src/ImportExportType.cs create mode 100644 target-dotnet/src/JsonWebKey.cs create mode 100644 target-dotnet/src/SignResult.cs create mode 100644 target-dotnet/src/WalletAccount.cs create mode 100644 target-dotnet/src/WalletKeyType.cs create mode 100644 target-dotnet/test/MaskWalletCoreTest.cs delete mode 100644 target-dotnet/test/PersonaKeyTest.cs diff --git a/target-dotnet/src/CoinType.cs b/target-dotnet/src/CoinType.cs new file mode 100644 index 0000000..ff03d17 --- /dev/null +++ b/target-dotnet/src/CoinType.cs @@ -0,0 +1,24 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public enum CoinType +{ + Ethereum, + Polkadot, + Solana +} + +internal static class CoinTypeExt +{ + public static Coin ToCoin(this CoinType coinType) + { + return coinType switch + { + CoinType.Ethereum => Coin.Ethereum, + CoinType.Polkadot => Coin.Polkadot, + CoinType.Solana => Coin.Solana, + _ => throw new ArgumentOutOfRangeException(nameof(coinType), coinType, null) + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/CurveType.cs b/target-dotnet/src/CurveType.cs new file mode 100644 index 0000000..ef1b684 --- /dev/null +++ b/target-dotnet/src/CurveType.cs @@ -0,0 +1,22 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public enum CurveType +{ + Secp256k1, + Ed25519 +} + +internal static class CurveTypeExt +{ + public static PersonaGenerationParam.Curve ToCurve(this CurveType type) + { + return type switch + { + CurveType.Secp256k1 => PersonaGenerationParam.Curve.Secp256k1, + CurveType.Ed25519 => PersonaGenerationParam.Curve.Ed25519, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/EncryptionOption.cs b/target-dotnet/src/EncryptionOption.cs new file mode 100644 index 0000000..ab65620 --- /dev/null +++ b/target-dotnet/src/EncryptionOption.cs @@ -0,0 +1,33 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public record EncryptionOption(EncryptionOption.EncVersion Version) +{ + public enum EncVersion + { + V37, + V38 + } + + public EncryptOption ToEncryptOption() + { + return new EncryptOption + { + version = Version.ToVersion() + }; + } +} + +internal static class EncVersionExt +{ + public static EncryptOption.Version ToVersion(this EncryptionOption.EncVersion version) + { + return version switch + { + EncryptionOption.EncVersion.V37 => EncryptOption.Version.V37, + EncryptionOption.EncVersion.V38 => EncryptOption.Version.V38, + _ => throw new ArgumentOutOfRangeException(nameof(version), version, null) + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/IWalletSignInput.cs b/target-dotnet/src/IWalletSignInput.cs new file mode 100644 index 0000000..026c6ce --- /dev/null +++ b/target-dotnet/src/IWalletSignInput.cs @@ -0,0 +1,37 @@ +using Ethereum; + +namespace Dimension.MaskWalletCore; + +public interface IWalletSignInput +{ + SignInput GetSignInput(); +} + +public record EthereumSignInput( + string Amount, + ulong ChainId, + string GasLimit, + string GasPrice, + string Nonce, + byte[] Payload, + string ToAddress, + string MaxInclusionFeePerGas, + string MaxFeePerGas +) : IWalletSignInput +{ + public SignInput GetSignInput() + { + return new SignInput + { + Amount = Amount, + ChainId = ChainId, + GasLimit = GasLimit, + GasPrice = GasPrice, + Nonce = Nonce, + Payload = Payload, + ToAddress = ToAddress, + MaxInclusionFeePerGas = MaxInclusionFeePerGas, + MaxFeePerGas = MaxFeePerGas + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/IWalletValidate.cs b/target-dotnet/src/IWalletValidate.cs new file mode 100644 index 0000000..a9a82bb --- /dev/null +++ b/target-dotnet/src/IWalletValidate.cs @@ -0,0 +1,71 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public interface IWalletValidate +{ + ValidateParam GetValidateParam(); +} + +public record PrivateKeyValidation(string PrivateKey) : IWalletValidate +{ + public ValidateParam GetValidateParam() + { + return new ValidateParam + { + privateKey = PrivateKey + }; + } +} + +public record MnemonicValidation(string Mnemonic) : IWalletValidate +{ + public ValidateParam GetValidateParam() + { + return new ValidateParam + { + Mnemonic = Mnemonic + }; + } +} + +public record KeyStoreJsonValidation(string KeyStoreJson) : IWalletValidate +{ + public ValidateParam GetValidateParam() + { + return new ValidateParam + { + keyStoreJSON = KeyStoreJson + }; + } +} + +public record StoredKeyValidation(byte[] StoredKey, string Password) : IWalletValidate +{ + public ValidateParam GetValidateParam() + { + return new ValidateParam + { + storedKeyPassword = new PasswordValidationParam + { + Password = Password, + storedKeyData = StoredKey + } + }; + } +} + +public record AddressValidation(string Address, CoinType Coin) : IWalletValidate +{ + public ValidateParam GetValidateParam() + { + return new ValidateParam + { + addressValidationParam = new AddressValidationParam + { + Address = Address, + Coin = Coin.ToCoin() + } + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/ImportExportType.cs b/target-dotnet/src/ImportExportType.cs new file mode 100644 index 0000000..059b780 --- /dev/null +++ b/target-dotnet/src/ImportExportType.cs @@ -0,0 +1,34 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public enum ImportExportType +{ + PrivateKey, + Mnemonic, + KeyStoreJSON +} + +internal static class ImportExportTypeExt +{ + public static StoredKeyImportType ToImportType(this ImportExportType type) + { + return type switch + { + ImportExportType.PrivateKey => StoredKeyImportType.PrivateKeyImportType, + ImportExportType.Mnemonic => StoredKeyImportType.MnemonicImportType, + ImportExportType.KeyStoreJSON => StoredKeyImportType.KeyStoreJSONImportType + }; + } + + public static ImportExportType FromImportType(this StoredKeyImportType type) + { + return type switch + { + StoredKeyImportType.PrivateKeyImportType => ImportExportType.PrivateKey, + StoredKeyImportType.MnemonicImportType => ImportExportType.Mnemonic, + StoredKeyImportType.KeyStoreJSONImportType => ImportExportType.KeyStoreJSON, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } +} \ No newline at end of file diff --git a/target-dotnet/src/JsonWebKey.cs b/target-dotnet/src/JsonWebKey.cs new file mode 100644 index 0000000..61194e5 --- /dev/null +++ b/target-dotnet/src/JsonWebKey.cs @@ -0,0 +1,52 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public record JsonWebKey( + string? kty = null, + string? kid = null, + string? use = null, + List? key_ops = null, + string? alg = null, + bool? ext = null, + string? crv = null, + string? x = null, + string? y = null, + string? d = null, + string? n = null, + string? e = null, + string? p = null, + string? q = null, + string? dp = null, + string? dq = null, + string? qi = null, + List? oth = null, + string? k = null +) +{ + public static JsonWebKey FromJWKResp(JWKResp resp) + { + return new JsonWebKey( + resp.Kty, + key_ops: resp.KeyOps, + ext: resp.Ext, + crv: resp.Crv, + x: resp.X, + y: resp.Y, + d: resp.D + ); + } + + public static JsonWebKey FromAesJWKResp(AesJWKResp resp) + { + return new JsonWebKey( + resp.Kty, + key_ops: resp.KeyOps, + alg: resp.Alg, + ext: resp.Ext, + k: resp.K + ); + } + + public record RsaOtherPrimesInfo(string r, string d, string t); +} \ No newline at end of file diff --git a/target-dotnet/src/MaskWalletCore.csproj b/target-dotnet/src/MaskWalletCore.csproj index 2633f89..9a9b1b8 100644 --- a/target-dotnet/src/MaskWalletCore.csproj +++ b/target-dotnet/src/MaskWalletCore.csproj @@ -1,54 +1,54 @@ - - net6.0 - enable - enable - Dimension.MaskWalletCore - MaskWalletCore - Dimension - https://github.com/DimensionDev/MaskWalletCore - https://raw.githubusercontent.com/DimensionDev/MaskWalletCore/master/LICENSE - + + net6.0 + enable + enable + Dimension.MaskWalletCore + MaskWalletCore + Dimension + https://github.com/DimensionDev/MaskWalletCore + https://raw.githubusercontent.com/DimensionDev/MaskWalletCore/master/LICENSE + - - - - - - - - - - - - - + + + - - ../rust - - - - - - - - - - PreserveNewest - .\lib - + + - - - - - - PreserveNewest - .\lib - + + + + + - + + + ../rust + + + + + + + + + + PreserveNewest + .\lib + + + + + + + + PreserveNewest + .\lib + + + diff --git a/target-dotnet/src/Native.cs b/target-dotnet/src/Native.cs index 838d1e8..a336049 100644 --- a/target-dotnet/src/Native.cs +++ b/target-dotnet/src/Native.cs @@ -1,32 +1,30 @@ +using System.Runtime.InteropServices; +using Api; using ProtoBuf; namespace Dimension.MaskWalletCore; -using System.Runtime.InteropServices; - internal class Native { - public static Api.MWResponse Call(Api.MWRequest request) + private const string LibName = "maskwalletcore_dotnet"; + + public static MWResponse Call(MWRequest request) { using var stream = new MemoryStream(); Serializer.Serialize(stream, request); - stream.Position = 0L; var data = stream.ToArray(); using var slice = rust_request(data, Convert.ToUInt64(data.Length)); - var buffer = new byte[slice.len]; - Marshal.Copy(slice.bytes, buffer, 0, buffer.Length); + var buffer = slice.AsByteArray(); using var responseStream = new MemoryStream(buffer); - responseStream.Position = 0L; - var result = Serializer.Deserialize(responseStream); + var result = Serializer.Deserialize(responseStream); if (result.Error != null) { throw new MaskWalletCoreException(result.Error.errorMsg); } + return result; } - private const string LibName = "maskwalletcore_dotnet"; - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] private static extern RustByteSlice rust_request([In] byte[] data, ulong size); } \ No newline at end of file diff --git a/target-dotnet/src/PersonaKey.cs b/target-dotnet/src/PersonaKey.cs index e743d42..ea216bd 100644 --- a/target-dotnet/src/PersonaKey.cs +++ b/target-dotnet/src/PersonaKey.cs @@ -2,55 +2,6 @@ namespace Dimension.MaskWalletCore; -public enum CurveType -{ - Secp256k1, - Ed25519, -} - -internal static class CurveTypeExt -{ - public static PersonaGenerationParam.Curve ToCurve(this CurveType type) - { - return type switch - { - CurveType.Secp256k1 => PersonaGenerationParam.Curve.Secp256k1, - CurveType.Ed25519 => PersonaGenerationParam.Curve.Ed25519, - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - } -} - -public record EncryptionOption(EncryptionOption.EncVersion Version) -{ - public enum EncVersion - { - V37, - V38, - } - public EncryptOption ToEncryptOption() - { - return new EncryptOption - { - version = this.Version.ToVersion(), - }; - } -} - - -internal static class EncVersionExt -{ - public static EncryptOption.Version ToVersion(this EncryptionOption.EncVersion version) - { - return version switch - { - EncryptionOption.EncVersion.V37 => EncryptOption.Version.V37, - EncryptionOption.EncVersion.V38 => EncryptOption.Version.V38, - _ => throw new ArgumentOutOfRangeException(nameof(version), version, null) - }; - } -} - public class PersonaKey { private readonly PersonaGenerationResp _resp; @@ -60,7 +11,16 @@ private PersonaKey(PersonaGenerationResp resp) _resp = resp; } - public static PersonaKey Create(string mnemonic, string password, string path, CurveType curveType, EncryptionOption option) + public string Identifier => _resp.Identifier; + + public JsonWebKey PrivateKey => JsonWebKey.FromJWKResp(_resp.privateKey); + + public JsonWebKey PublicKey => JsonWebKey.FromJWKResp(_resp.publicKey); + + public JsonWebKey? LocalKey => _resp.localKey != null ? JsonWebKey.FromAesJWKResp(_resp.localKey) : null; + + public static PersonaKey Create(string mnemonic, string password, string path, CurveType curveType, + EncryptionOption option) { var resp = Native.Call(new MWRequest { @@ -70,11 +30,9 @@ public static PersonaKey Create(string mnemonic, string password, string path, C Password = password, Path = path, curve = curveType.ToCurve(), - Option = option.ToEncryptOption(), + Option = option.ToEncryptOption() } }).RespGeneratePersona; return new PersonaKey(resp); } - - public string Identifier => _resp.Identifier; } \ No newline at end of file diff --git a/target-dotnet/src/RustByteSlice.cs b/target-dotnet/src/RustByteSlice.cs index f629b63..980edc2 100644 --- a/target-dotnet/src/RustByteSlice.cs +++ b/target-dotnet/src/RustByteSlice.cs @@ -12,8 +12,16 @@ public void Dispose() { rust_free(this); } - + + public byte[] AsByteArray() + { + var buffer = new byte[len]; + Marshal.Copy(bytes, buffer, 0, buffer.Length); + return buffer; + } + private const string LibName = "maskwalletcore_dotnet"; + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] public static extern void rust_free(RustByteSlice data); -} +} \ No newline at end of file diff --git a/target-dotnet/src/SignResult.cs b/target-dotnet/src/SignResult.cs new file mode 100644 index 0000000..0d26c24 --- /dev/null +++ b/target-dotnet/src/SignResult.cs @@ -0,0 +1,3 @@ +namespace Dimension.MaskWalletCore; + +public record SignResult(byte[] Encoded, uint V, byte[] R, byte[] S, byte[] Data); \ No newline at end of file diff --git a/target-dotnet/src/WalletAccount.cs b/target-dotnet/src/WalletAccount.cs new file mode 100644 index 0000000..2809c68 --- /dev/null +++ b/target-dotnet/src/WalletAccount.cs @@ -0,0 +1,19 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public class WalletAccount +{ + private readonly StoredKeyAccountInfo _account; + + internal WalletAccount(StoredKeyAccountInfo account) + { + _account = account; + } + + public string Address => _account.Address; + public string Name => _account.Name; + public string Coin => _account.Coin; + public string DerivationPath => _account.derivationPath; + public string ExtendedPublicKey => _account.extendedPublicKey; +} \ No newline at end of file diff --git a/target-dotnet/src/WalletKey.cs b/target-dotnet/src/WalletKey.cs index 4766b22..a637c9d 100644 --- a/target-dotnet/src/WalletKey.cs +++ b/target-dotnet/src/WalletKey.cs @@ -1,9 +1,24 @@ +using System.Collections.Immutable; using Api; namespace Dimension.MaskWalletCore; +public record CreateKeyResult(WalletKey Key, string mnemonic); + public class WalletKey { + private StoredKeyInfo _keyInfo; + + private WalletKey(StoredKeyInfo keyInfo) + { + _keyInfo = keyInfo; + } + + public string Id => _keyInfo.Id; + public string Hash => _keyInfo.Hash; + public WalletKeyType Type => _keyInfo.Type.From(); + public byte[] Data => _keyInfo.Data; + public static string GenerateMnemonic() { return Native.Call(new MWRequest @@ -11,29 +26,216 @@ public static string GenerateMnemonic() ParamGenerateMnemonic = new GenerateMnemonicParam() }).RespGenerateMnemonic.Mnemonic; } -} - -public record JsonWebKey( - string? kty, - string? kid, - string? use, - List? key_ops, - string? alg, - Boolean? ext, - string? crv, - string? x, - string? y, - string? d, - string? n, - string? e, - string? p, - string? q, - string? dp, - string? dq, - string? qi, - List? oth, - string? k -) -{ - public record RsaOtherPrimesInfo(string r, string d, string t); + + public static WalletKey? Load(byte[] key) + { + return Native.Call(new MWRequest + { + ParamLoadStoredKey = new LoadStoredKeyParam + { + Datas = { key } + } + }).RespLoadStoredKey.StoredKeys.Select(it => new WalletKey(it)).FirstOrDefault(); + } + + public static CreateKeyResult Create(string password) + { + var result = Native.Call(new MWRequest + { + ParamCreateStoredKey = new CreateStoredKeyParam + { + Password = password + } + }).RespCreateStoredKey; + return new CreateKeyResult(new WalletKey(result.StoredKey), result.Mnemonic); + } + + public static WalletKey FromMnemonic(string mnemonic, string password) + { + return new WalletKey(Native.Call(new MWRequest + { + ParamImportMnemonic = new ImportMnemonicStoredKeyParam + { + Mnemonic = mnemonic, + Password = password + } + }).RespImportMnemonic.StoredKey); + } + + public static WalletKey FromJson(string json, string password, string name, string keyStoreJsonPassword, + CoinType coin) + { + return new WalletKey(Native.Call(new MWRequest + { + ParamImportJson = new ImportJSONStoredKeyParam + { + Json = json, + Password = password, + Name = name, + keyStoreJsonPassword = keyStoreJsonPassword, + Coin = coin.ToCoin() + } + }).RespImportJson.StoredKey); + } + + public static WalletKey FromPrivateKey(string privateKey, string password, string name, CoinType coin) + { + return new WalletKey(Native.Call(new MWRequest + { + ParamImportPrivateKey = new ImportPrivateStoredKeyParam + { + privateKey = privateKey, + Password = password, + Name = name, + Coin = coin.ToCoin() + } + }).RespImportPrivateKey.StoredKey); + } + + public static bool Validate(IWalletValidate walletValidate) + { + return Native.Call(new MWRequest + { + ParamValidation = walletValidate.GetValidateParam() + }).RespValidate.Valid; + } + + public static IReadOnlyCollection SupportedImportType(CoinType coinType) + { + return Native.Call(new MWRequest + { + ParamGetStoredKeyImportType = new GetKeyStoreSupportImportTypeParam + { + Coin = coinType.ToCoin() + } + }).RespGetStoredKeyImportType.Types.Select(it => it.FromImportType()).ToImmutableList(); + } + + public static IReadOnlyCollection SupportedExportType(CoinType coinType) + { + return Native.Call(new MWRequest + { + ParamGetStoredKeyExportType = new GetKeyStoreSupportExportTypeParam + { + Coin = coinType.ToCoin() + } + }).RespGetStoredKeyExportType.Types.Select(it => it.FromImportType()).ToImmutableList(); + } + + public WalletAccount AddNewAccountAtPath(CoinType coinType, string derivationPath, string name, string password) + { + var result = Native.Call(new MWRequest + { + ParamCreateAccountOfCoinAtPath = new CreateStoredKeyNewAccountAtPathParam + { + Name = name, + Password = password, + Coin = coinType.ToCoin(), + derivationPath = derivationPath, + StoredKeyData = _keyInfo.Data + } + }).RespCreateAccountOfCoinAtPath; + _keyInfo = result.storedKey; + return new WalletAccount(result.Account); + } + + public string ExportMnemonic(string password) + { + return Native.Call(new MWRequest + { + ParamExportMnemonic = new ExportKeyStoreMnemonicParam + { + Password = password, + StoredKeyData = _keyInfo.Data + } + }).RespExportMnemonic.Mnemonic; + } + + public string ExportPrivateKey(string password, CoinType coinType) + { + return Native.Call(new MWRequest + { + ParamExportPrivateKey = new ExportKeyStorePrivateKeyParam + { + Password = password, + StoredKeyData = _keyInfo.Data, + Coin = coinType.ToCoin() + } + }).RespExportPrivateKey.privateKey; + } + + public string ExportPrivateKeyAtPath(string password, string derivationPath, CoinType coinType) + { + return Native.Call(new MWRequest + { + ParamExportPrivateKeyOfPath = new ExportKeyStorePrivateKeyOfPathParam + { + Password = password, + StoredKeyData = _keyInfo.Data, + Coin = coinType.ToCoin(), + derivationPath = derivationPath + } + }).RespExportPrivateKey.privateKey; + } + + public string ExportKeyStoreJsonOfAddress(string address, CoinType coinType, string password, string newPassword) + { + return Native.Call(new MWRequest + { + ParamExportKeyStoreJsonOfAddress = new ExportKeyStoreJSONOfAddressParam + { + Password = password, + StoredKeyData = _keyInfo.Data, + Coin = coinType.ToCoin(), + Address = address, + newPassword = newPassword + } + }).RespExportKeyStoreJson.Json; + } + + public string ExportKeyStoreJsonOfPath(string derivationPath, CoinType coinType, string password, + string newPassword) + { + return Native.Call(new MWRequest + { + ParamExportKeyStoreJsonOfPath = new ExportKeyStoreJSONOfPathParam + { + Password = password, + StoredKeyData = _keyInfo.Data, + Coin = coinType.ToCoin(), + derivationPath = derivationPath, + newPassword = newPassword + } + }).RespExportKeyStoreJson.Json; + } + + public void UpdatePassword(string oldPassword, string newPassword) + { + var result = Native.Call(new MWRequest + { + ParamUpdateKeyStorePassword = new UpdateStoredKeyPasswordParam + { + StoredKeyData = _keyInfo.Data, + newPassword = newPassword, + oldPassword = oldPassword + } + }).RespUpdateKeyStorePassword; + _keyInfo = result.StoredKey; + } + + public SignResult Sign(string derivationPath, string password, CoinType coinType, IWalletSignInput input) + { + var result = Native.Call(new MWRequest + { + ParamSignTransaction = new SignTransactionParam + { + storedKeyData = _keyInfo.Data, + Coin = coinType.ToCoin(), + derivationPath = derivationPath, + Password = password, + SignInput = input.GetSignInput() + } + }).RespSignTransaction.SignOutput; + return new SignResult(result.Encoded, result.V, result.R, result.S, result.Data); + } } \ No newline at end of file diff --git a/target-dotnet/src/WalletKeyType.cs b/target-dotnet/src/WalletKeyType.cs new file mode 100644 index 0000000..082c592 --- /dev/null +++ b/target-dotnet/src/WalletKeyType.cs @@ -0,0 +1,22 @@ +using Api; + +namespace Dimension.MaskWalletCore; + +public enum WalletKeyType +{ + PrivateKey, + Mnemonic +} + +internal static class WalletKeyTypeExt +{ + public static WalletKeyType From(this StoredKeyType type) + { + return type switch + { + StoredKeyType.PrivateKey => WalletKeyType.PrivateKey, + StoredKeyType.Mnemonic => WalletKeyType.Mnemonic, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } +} \ No newline at end of file diff --git a/target-dotnet/test/MaskWalletCoreTest.cs b/target-dotnet/test/MaskWalletCoreTest.cs new file mode 100644 index 0000000..5f6aa24 --- /dev/null +++ b/target-dotnet/test/MaskWalletCoreTest.cs @@ -0,0 +1,42 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dimension.MaskWalletCore; + +[TestClass] +public class MaskWalletCoreTest +{ + [TestMethod] + public void TestWalletKeyCreation() + { + const string password = "password"; + var result = WalletKey.Create(password); + Assert.IsFalse(string.IsNullOrEmpty(result.mnemonic)); + Assert.IsFalse(string.IsNullOrWhiteSpace(result.mnemonic)); + Assert.IsFalse(string.IsNullOrEmpty(result.Key.Id)); + Assert.IsFalse(string.IsNullOrEmpty(result.Key.Hash)); + } + + [TestMethod] + public void TestWalletRestoreByMnemonic() + { + const string password = "password"; + var result = WalletKey.Create(password); + var wallet = WalletKey.FromMnemonic(result.mnemonic, password); + Assert.IsNotNull(wallet); + Assert.IsFalse(string.IsNullOrEmpty(wallet.Id)); + Assert.IsFalse(string.IsNullOrEmpty(wallet.Hash)); + } + + [TestMethod] + public void TestPersonaCreate() + { + const string password = "123456"; + var mnemonic = WalletKey.GenerateMnemonic(); + const string path = "m/44'/60'/0'/0/0"; + const CurveType curveType = CurveType.Secp256k1; + var option = new EncryptionOption(EncryptionOption.EncVersion.V38); + var key = PersonaKey.Create(mnemonic, password, path, curveType, option); + Assert.IsFalse(string.IsNullOrEmpty(key.Identifier)); + Assert.IsTrue(key.Identifier.StartsWith("ec_key:secp256k1/")); + } +} \ No newline at end of file diff --git a/target-dotnet/test/MaskWalletCoreTest.csproj b/target-dotnet/test/MaskWalletCoreTest.csproj index 6717c0c..ffafb5b 100644 --- a/target-dotnet/test/MaskWalletCoreTest.csproj +++ b/target-dotnet/test/MaskWalletCoreTest.csproj @@ -1,20 +1,20 @@ - - net6.0 - enable - false - Dimension.MaskWalletCore - + + net6.0 + enable + false + Dimension.MaskWalletCore + - - - - - - + + + + + + - - - + + + diff --git a/target-dotnet/test/PersonaKeyTest.cs b/target-dotnet/test/PersonaKeyTest.cs deleted file mode 100644 index 118d209..0000000 --- a/target-dotnet/test/PersonaKeyTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dimension.MaskWalletCore; - -[TestClass] -public class PersonaKeyTest -{ - [TestMethod] - public void TestCreate() - { - const string password = "123456"; - var mnemonic = WalletKey.GenerateMnemonic(); - const string path = "m/44'/60'/0'/0/0"; - const CurveType curveType = CurveType.Secp256k1; - var option = new EncryptionOption(EncryptionOption.EncVersion.V38); - var key = PersonaKey.Create(mnemonic, password, path, curveType, option); - Assert.IsTrue(!string.IsNullOrEmpty(key.Identifier)); - } -} \ No newline at end of file From ec7a577690f5ad0052ae9dad44a036a6e369f3b0 Mon Sep 17 00:00:00 2001 From: Tlaster Date: Thu, 21 Apr 2022 18:02:01 +0800 Subject: [PATCH 3/3] add dotnet ci --- .github/workflows/dotnet.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/dotnet.yaml diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml new file mode 100644 index 0000000..f5b7b75 --- /dev/null +++ b/.github/workflows/dotnet.yaml @@ -0,0 +1,33 @@ +name: .NET Core + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + - name: Install dependencies + working-directory: ./target-dotnet + run: dotnet restore + - name: Build + working-directory: ./target-dotnet + run: dotnet build --configuration Release --no-restore + - name: Test + working-directory: ./target-dotnet + run: dotnet test --no-restore --verbosity normal