diff --git a/HDWallet.sln b/HDWallet.sln index 2d4109e..2bb43c5 100644 --- a/HDWallet.sln +++ b/HDWallet.sln @@ -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 @@ -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 @@ -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} diff --git a/src/HDWallet.Core/CoinType.cs b/src/HDWallet.Core/CoinType.cs index 65a8740..c4f518a 100644 --- a/src/HDWallet.Core/CoinType.cs +++ b/src/HDWallet.Core/CoinType.cs @@ -15,6 +15,8 @@ public enum CoinType : uint FileCoin = 461, Solana = 501, Blockstack = 5757, - Neo = 888 + Neo = 888, + Cosmos = 118, + Terra = 330 } } \ No newline at end of file diff --git a/src/HDWallet.Cosmos/AddressGenerator.cs b/src/HDWallet.Cosmos/AddressGenerator.cs new file mode 100644 index 0000000..cf53e9a --- /dev/null +++ b/src/HDWallet.Cosmos/AddressGenerator.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/HDWallet.Cosmos/CosmosHDWalletSecp256k1.cs b/src/HDWallet.Cosmos/CosmosHDWalletSecp256k1.cs new file mode 100644 index 0000000..1849420 --- /dev/null +++ b/src/HDWallet.Cosmos/CosmosHDWalletSecp256k1.cs @@ -0,0 +1,27 @@ +using HDWallet.Core; +using HDWallet.Secp256k1; + +namespace HDWallet.Cosmos +{ + public class CosmosHDWalletSecp256k1 : HDWallet + { + 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)) { } + + /// + /// Generates Account from master. Doesn't derive new path by accountIndexInfo + /// + /// Used to generate wallet + /// Used only to store information + /// + public static IAccount GetAccountFromMasterKey(string accountMasterKey, uint accountIndexInfo) + { + IAccountHDWallet accountHDWallet = new AccountHDWallet(accountMasterKey, accountIndexInfo); + return accountHDWallet.Account; + } + } +} \ No newline at end of file diff --git a/src/HDWallet.Cosmos/CosmosWallet.cs b/src/HDWallet.Cosmos/CosmosWallet.cs new file mode 100644 index 0000000..ccd7b2d --- /dev/null +++ b/src/HDWallet.Cosmos/CosmosWallet.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/src/HDWallet.Cosmos/HDWallet.Cosmos.csproj b/src/HDWallet.Cosmos/HDWallet.Cosmos.csproj new file mode 100644 index 0000000..70c83db --- /dev/null +++ b/src/HDWallet.Cosmos/HDWallet.Cosmos.csproj @@ -0,0 +1,35 @@ + + + + netcoreapp3.1 + + + + 0.1.1 + + git + https://github.com/farukterzioglu/HDWallet + https://github.com/farukterzioglu/HDWallet + + MIT + HDWallet;Cosmos;Blockchain;Secp256k1 + HDWallet.Cosmos + + Faruk Terzioglu + + + + + + + + + + + + + + + + + diff --git a/src/HDWallet.Cosmos/README.md b/src/HDWallet.Cosmos/README.md new file mode 100644 index 0000000..f654c7e --- /dev/null +++ b/src/HDWallet.Cosmos/README.md @@ -0,0 +1 @@ +### \ No newline at end of file diff --git a/test/HDWallet.Cosmos.Tests/GenerateWallet.cs b/test/HDWallet.Cosmos.Tests/GenerateWallet.cs new file mode 100644 index 0000000..4aeba9a --- /dev/null +++ b/test/HDWallet.Cosmos.Tests/GenerateWallet.cs @@ -0,0 +1,22 @@ +using System; +using HDWallet.Core; +using NUnit.Framework; + +namespace HDWallet.Cosmos.Tests +{ + public class GenerateWallet + { + + [Test] + public void ShouldGenerateWalletFromMnemonic() + { + IHDWallet 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"); + } + } +} \ No newline at end of file diff --git a/test/HDWallet.Cosmos.Tests/HDWallet.Cosmos.Tests.csproj b/test/HDWallet.Cosmos.Tests/HDWallet.Cosmos.Tests.csproj new file mode 100644 index 0000000..471a736 --- /dev/null +++ b/test/HDWallet.Cosmos.Tests/HDWallet.Cosmos.Tests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + +