Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for Cosmos. #35

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions HDWallet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HDWallet.Secp256r1.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HDWallet.Secp256", "src\HDWallet.Secp256\HDWallet.Secp256.csproj", "{86D54B89-447D-4168-8CC9-CE5C09AFFC2F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HDWallet.Cosmos", "src\HDWallet.Cosmos\HDWallet.Cosmos.csproj", "{6389137E-388B-41A2-B76B-563557303C87}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HDWallet.Cosmos.Tests", "test\HDWallet.Cosmos.Tests\HDWallet.Cosmos.Tests.csproj", "{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -465,6 +469,30 @@ Global
{86D54B89-447D-4168-8CC9-CE5C09AFFC2F}.Release|x64.Build.0 = Release|Any CPU
{86D54B89-447D-4168-8CC9-CE5C09AFFC2F}.Release|x86.ActiveCfg = Release|Any CPU
{86D54B89-447D-4168-8CC9-CE5C09AFFC2F}.Release|x86.Build.0 = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|x64.ActiveCfg = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|x64.Build.0 = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|x86.ActiveCfg = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Debug|x86.Build.0 = Debug|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|Any CPU.Build.0 = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|x64.ActiveCfg = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|x64.Build.0 = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|x86.ActiveCfg = Release|Any CPU
{6389137E-388B-41A2-B76B-563557303C87}.Release|x86.Build.0 = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|x64.Build.0 = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Debug|x86.Build.0 = Debug|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|Any CPU.Build.0 = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|x64.ActiveCfg = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|x64.Build.0 = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|x86.ActiveCfg = Release|Any CPU
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -502,6 +530,8 @@ Global
{96BA746E-2F25-4ED9-AA60-4ECC66903DD0} = {587DF704-BD3F-41FF-A468-289BB83BBEF7}
{87F77813-6571-4245-AA71-D22AF0E9CB20} = {73CF8F5A-07F9-477C-8F47-507158E4C68C}
{86D54B89-447D-4168-8CC9-CE5C09AFFC2F} = {587DF704-BD3F-41FF-A468-289BB83BBEF7}
{6389137E-388B-41A2-B76B-563557303C87} = {587DF704-BD3F-41FF-A468-289BB83BBEF7}
{6D1AF7BA-FA9E-4372-B29F-E7DDC0BBAAB9} = {73CF8F5A-07F9-477C-8F47-507158E4C68C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A8055563-076B-434F-9578-B8CAA10E458E}
Expand Down
4 changes: 3 additions & 1 deletion src/HDWallet.Core/CoinType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum CoinType : uint
FileCoin = 461,
Solana = 501,
Blockstack = 5757,
Neo = 888
Neo = 888,
Cosmos = 118,
Terra = 330
}
}
68 changes: 68 additions & 0 deletions src/HDWallet.Cosmos/AddressGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Linq;
using HDWallet.Core;

namespace HDWallet.Cosmos
{
public class AddressGenerator : IAddressGenerator
{
string DefaultHRP { get; set; } = "cosmos";

string IAddressGenerator.GenerateAddress(byte[] pubKeyBytes)
{
return $"{GetBech32Address(pubKeyBytes, DefaultHRP)}";
}

public string GenerateAddress(byte[] pubKeyBytes, string hrp)
{
return $"{GetBech32Address(pubKeyBytes, hrp)}";
}

private string GetBech32Address(byte[] pubKeyBytes, string hrp)
{
var addr = addressFromPublicKey(pubKeyBytes);
return Bech32Engine.Encode(hrp, addr);
}

private byte[] addressFromPublicKey(byte[] pubKeyBytes)
{
if(pubKeyBytes.Length == 65)
{
throw new NotImplementedException();
}

if(pubKeyBytes.Length == 33)
{
var sha256 = NBitcoin.Crypto.Hashes.SHA256(pubKeyBytes);
var ripesha = NBitcoin.Crypto.Hashes.RIPEMD160(sha256, sha256.Length);
return ripesha;
}

throw new NotSupportedException();
}

public string GetBech32PublicKey(byte[] pubKeyBytes, string hrp)
{
var rawPubKey = pubKeyFromPublicKey(pubKeyBytes);
return Bech32Engine.Encode(hrp, rawPubKey);
}

// See: https://github.com/tendermint/tendermint/blob/d419fffe18531317c28c29a292ad7d253f6cafdf/docs/spec/blockchain/encoding.md#public-key-cryptography
const string BECH32_PUBKEY_DATA_PREFIX = "eb5ae98721";

private byte[] pubKeyFromPublicKey(byte[] pubKeyBytes)
{
var buffer = HexStringToByteArray(BECH32_PUBKEY_DATA_PREFIX);
var combined = buffer.Concat(pubKeyBytes).ToArray();
return combined;
}

public static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
}
27 changes: 27 additions & 0 deletions src/HDWallet.Cosmos/CosmosHDWalletSecp256k1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using HDWallet.Core;
using HDWallet.Secp256k1;

namespace HDWallet.Cosmos
{
public class CosmosHDWalletSecp256k1 : HDWallet<CosmosWallet>
{
private static readonly HDWallet.Core.CoinPath _path = Purpose.Create(PurposeNumber.BIP44).Coin(CoinType.Cosmos);

public CosmosHDWalletSecp256k1(string words, string seedPassword = "") : base(words, seedPassword, _path) { }

public CosmosHDWalletSecp256k1(CoinType coinType, string words, string seedPassword = "")
: base(words, seedPassword, Purpose.Create(PurposeNumber.BIP44).Coin(coinType)) { }

/// <summary>
/// Generates Account from master. Doesn't derive new path by accountIndexInfo
/// </summary>
/// <param name="accountMasterKey">Used to generate wallet</param>
/// <param name="accountIndexInfo">Used only to store information</param>
/// <returns></returns>
public static IAccount<CosmosWallet> GetAccountFromMasterKey(string accountMasterKey, uint accountIndexInfo)
{
IAccountHDWallet<CosmosWallet> accountHDWallet = new AccountHDWallet<CosmosWallet>(accountMasterKey, accountIndexInfo);
return accountHDWallet.Account;
}
}
}
32 changes: 32 additions & 0 deletions src/HDWallet.Cosmos/CosmosWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Linq;
using System.Text;
using HDWallet.Core;
using HDWallet.Secp256k1;
using NBitcoin;
using NBitcoin.DataEncoders;

namespace HDWallet.Cosmos
{
public class CosmosWallet : Wallet, IWallet
{
public CosmosWallet() { }

public CosmosWallet(string privateKey) : base(privateKey) { }

protected override IAddressGenerator GetAddressGenerator()
{
return new AddressGenerator();
}

public string GetAddress(string hrp)
{
return new AddressGenerator().GenerateAddress(PublicKey.ToBytes(), hrp);
}

public string GetPublicKey(string hrp)
{
return new AddressGenerator().GetBech32PublicKey(PublicKey.ToBytes(), hrp);
}
}
}
35 changes: 35 additions & 0 deletions src/HDWallet.Cosmos/HDWallet.Cosmos.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<Title></Title>
<Description></Description>
<Version>0.1.1</Version>

<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/farukterzioglu/HDWallet</RepositoryUrl>
<PackageProjectUrl>https://github.com/farukterzioglu/HDWallet</PackageProjectUrl>

<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>HDWallet;Cosmos;Blockchain;Secp256k1</PackageTags>
<PackageId>HDWallet.Cosmos</PackageId>

<Authors>Faruk Terzioglu</Authors>
<Company></Company>

</PropertyGroup>

<ItemGroup>
<PackageReference Include="Base58Check" Version="0.2.0" />
<PackageReference Include="Nethereum.Web3" Version="3.8.0" />
<PackageReference Include="Konscious.Security.Cryptography.Blake2" Version="1.0.9" />
<PackageReference Include="NBitcoin.Secp256k1" Version="1.0.1" />
<PackageReference Include="NBitcoin" Version="5.0.27" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\HDWallet.Secp256k1\HDWallet.Secp256k1.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/HDWallet.Cosmos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
###
22 changes: 22 additions & 0 deletions test/HDWallet.Cosmos.Tests/GenerateWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using HDWallet.Core;
using NUnit.Framework;

namespace HDWallet.Cosmos.Tests
{
public class GenerateWallet
{

[Test]
public void ShouldGenerateWalletFromMnemonic()
{
IHDWallet<CosmosWallet> cosmosHDWallet = new CosmosHDWalletSecp256k1(CoinType.Terra, "social laugh lecture orchard mind spend soap window hidden donor machine moment sketch own desk omit tag hold gaze name parade fly main obtain");
var account0 = cosmosHDWallet.GetAccount(0);
CosmosWallet wallet0 = account0.GetExternalWallet(0);
Assert.AreEqual("02b6465822d1fe2b27a10cbfd65f3e577517605b3df02152fa31e1f4994844a619", wallet0.PublicKey.ToHex());
Assert.AreEqual("b13443590343bdce4043f8442dce3831e758a0949d9fc5cf03d5cfc4058b1452", wallet0.PrivateKey.ToHex());
Assert.AreEqual("terra1av8hsx3wt8xnw9ksxtjvlywsa6yl4ghw2lth7x",wallet0.GetAddress("terra") );
var accountPublicKey = wallet0.GetPublicKey("terrapub");
}
}
}
19 changes: 19 additions & 0 deletions test/HDWallet.Cosmos.Tests/HDWallet.Cosmos.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\HDWallet.Cosmos\HDWallet.Cosmos.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
</ItemGroup>

</Project>