Skip to content

Commit

Permalink
Merge branch 'master' of github.com:farukterzioglu/HDWallet
Browse files Browse the repository at this point in the history
  • Loading branch information
farukterzioglu committed Apr 4, 2022
2 parents 0b2c469 + ff27e9d commit e0b9e81
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 15 deletions.
8 changes: 8 additions & 0 deletions src/HDWallet.Core/IPublicAccount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace HDWallet.Core
{
public interface IPublicAccount<out TWallet> where TWallet : IPublicWallet, new()
{
TWallet GetInternalPublicWallet(uint addressIndex);
TWallet GetExternalPublicWallet(uint addressIndex);
}
}
12 changes: 12 additions & 0 deletions src/HDWallet.Core/IPublicWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace HDWallet.Core
{
public interface IPublicWallet
{
string Address { get; }

byte[] PublicKeyBytes { get; set; }

// TODO: Add unit tests
bool Verify(byte[] message, Signature sig);
}
}
2 changes: 0 additions & 2 deletions src/HDWallet.Core/IWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ public interface IWallet

byte[] PublicKeyBytes { get; }

uint Index { get; set; }

bool Verify(byte[] message, Signature sig);
}
}
14 changes: 5 additions & 9 deletions src/HDWallet.Ed25519/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,28 @@ namespace HDWallet.Ed25519
/// <typeparam name="TWallet"></typeparam>
public class Account<TWallet> : IAccount<TWallet> where TWallet : Wallet, IWallet, new()
{
public uint AccountIndex { get; set; }
Func<string, TWallet> _deriveFunction;
readonly uint _accountIndex;
readonly Func<string, TWallet> _deriveFunction;

internal Account(uint accountIndex, Func<string, TWallet> deriveFunction)
{
AccountIndex = accountIndex;
_accountIndex = accountIndex;
_deriveFunction = deriveFunction;
}

TWallet IAccount<TWallet>.GetInternalWallet(uint addressIndex)
{
var internalKeyPath = $"{AccountIndex}'/1'/{addressIndex}'";
var internalKeyPath = $"{_accountIndex}'/1'/{addressIndex}'";
var internalWallet = _deriveFunction(internalKeyPath);

internalWallet.Index = addressIndex;

return internalWallet;
}

TWallet IAccount<TWallet>.GetExternalWallet(uint addressIndex)
{
var externalKeyPath = $"{AccountIndex}'/0'/{addressIndex}'";
var externalKeyPath = $"{_accountIndex}'/0'/{addressIndex}'";
var externalWallet = _deriveFunction(externalKeyPath);

externalWallet.Index = addressIndex;

return externalWallet;
}
}
Expand Down
1 change: 0 additions & 1 deletion src/HDWallet.Ed25519/Sample/CardanoHDWalletEd25519.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ CardanoSampleWallet GetRewardWallet(uint accountIndex)
var externalKeyPath = $"{accountIndex}'/2'/0'";

var rewardWallet = GetSubWallet(externalKeyPath);
rewardWallet.Index = 0;

return rewardWallet;
}
Expand Down
2 changes: 0 additions & 2 deletions src/HDWallet.Ed25519/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ namespace HDWallet.Ed25519
public abstract class Wallet : IWallet
{
public byte[] PublicKeyBytes { get; private set; }

public uint Index { get; set; }

public string Path {get; set;}

Expand Down
17 changes: 17 additions & 0 deletions src/HDWallet.Ethereum/EthereumPublicWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using HDWallet.Core;
using HDWallet.Secp256k1;

namespace HDWallet.Ethereum
{
public class EthereumPublicWallet : PublicWallet, IPublicWallet
{
public EthereumPublicWallet() {}
public EthereumPublicWallet(string publicKey) : base(publicKey: publicKey) {}
public EthereumPublicWallet(byte[] publicKeyBytes) : base(publicKeyBytes: publicKeyBytes) {}

protected override IAddressGenerator GetAddressGenerator()
{
return new AddressGenerator();
}
}
}
41 changes: 41 additions & 0 deletions src/HDWallet.Secp256/PublicAccountSecpBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using HDWallet.Core;
using NBitcoin;

namespace HDWallet.Secp256
{
// TODO: Add unit tests
public class PublicAccountSecpBase<TWallet> : IPublicAccount<TWallet> where TWallet : IPublicWallet, new()
{
readonly BitcoinExtPubKey _bitcoinExtPubKey;

/// <summary>
///
/// </summary>
/// <param name="accountMasterPublicKey"></param>
/// <param name="network"></param>
public PublicAccountSecpBase(string accountMasterPublicKey, Network network)
{
_bitcoinExtPubKey = new BitcoinExtPubKey(accountMasterPublicKey, network);
}

TWallet IPublicAccount<TWallet>.GetInternalPublicWallet(uint addressIndex)
{
ExtPubKey extKey = _bitcoinExtPubKey.ExtPubKey.Derive(new KeyPath($"1/{addressIndex}"));

return new TWallet()
{
PublicKeyBytes = extKey.PubKey.ToBytes()
};
}

TWallet IPublicAccount<TWallet>.GetExternalPublicWallet(uint addressIndex)
{
ExtPubKey extKey = _bitcoinExtPubKey.ExtPubKey.Derive(new KeyPath($"0/{addressIndex}"));

return new TWallet()
{
PublicKeyBytes = extKey.PubKey.ToBytes()
};
}
}
}
11 changes: 11 additions & 0 deletions src/HDWallet.Secp256k1/PublicAccount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using HDWallet.Core;
using HDWallet.Secp256;
using NBitcoin;

namespace HDWallet.Secp256k1
{
public class PublicAccount<TWallet> : PublicAccountSecpBase<TWallet>, IPublicAccount<TWallet> where TWallet : PublicWallet, new()
{
public PublicAccount(string accountMasterPublicKey, Network network): base(accountMasterPublicKey, network) {}
}
}
59 changes: 59 additions & 0 deletions src/HDWallet.Secp256k1/PublicWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using HDWallet.Core;
using NBitcoin;
using NBitcoin.Crypto;

namespace HDWallet.Secp256k1
{
public abstract class PublicWallet : IPublicWallet
{
PubKey _publicKey;
readonly IAddressGenerator _addressGenerator;

private byte[] _publicKeyBytes;

public byte[] PublicKeyBytes
{
get
{
return _publicKeyBytes;
}
set
{
PubKey.TryCreatePubKey(value, out _publicKey);
_publicKeyBytes = value;
}
}

string IPublicWallet.Address => _addressGenerator.GenerateAddress(_publicKey.ToBytes());

public PublicWallet()
{
_addressGenerator = GetAddressGenerator();
}

public PublicWallet(string publicKey) : this()
{
_publicKey = new PubKey(publicKey);
_publicKeyBytes = _publicKey.ToBytes();

_addressGenerator = GetAddressGenerator();
}

public PublicWallet(byte[] publicKeyBytes)
{
PubKey.TryCreatePubKey(publicKeyBytes, out _publicKey);
_publicKeyBytes = publicKeyBytes;

_addressGenerator = GetAddressGenerator();
}

bool IPublicWallet.Verify(byte[] message, Signature sig)
{
ECDSASignature.TryParseFromCompact(sig.ToCompact(), out ECDSASignature signature);
return _publicKey.Verify(new uint256(message), signature);
}

protected abstract IAddressGenerator GetAddressGenerator();
}
}
16 changes: 16 additions & 0 deletions src/HDWallet.Secp256k1/Sample/SampleSecp256k1PublicWallet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using HDWallet.Core;

namespace HDWallet.Secp256k1.Sample
{
public class SampleSecp256k1PublicWallet : PublicWallet, IPublicWallet
{
public SampleSecp256k1PublicWallet(){}

public SampleSecp256k1PublicWallet(byte[] publicKeyBytes) : base(publicKeyBytes) {}

protected override IAddressGenerator GetAddressGenerator()
{
return new NullAddressGenerator();
}
}
}
2 changes: 1 addition & 1 deletion src/HDWallet.Secp256k1/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace HDWallet.Secp256k1
public abstract class Wallet : IWallet
{
public Key PrivateKey;

public PubKey PublicKey => PrivateKey.PubKey;

public uint Index { get; set; }
public string Address => AddressGenerator.GenerateAddress(PublicKey.ToBytes());

private byte[] privateKeyBytes;
Expand Down
12 changes: 12 additions & 0 deletions src/HDWallet.Tron/TronWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,16 @@ protected override IAddressGenerator GetAddressGenerator()
return new AddressGenerator();
}
}

public class TronPublicWallet : PublicWallet, IPublicWallet
{
public TronPublicWallet(){}

public TronPublicWallet(string publicKey) : base(publicKey) {}

protected override IAddressGenerator GetAddressGenerator()
{
return new AddressGenerator();
}
}
}
40 changes: 40 additions & 0 deletions test/HDWallet.Core.Tests/HdWalletBaseTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using NBitcoin;
using NUnit.Framework;

namespace HDWallet.Core.Tests
Expand Down Expand Up @@ -46,5 +47,44 @@ public void ShouldCreateFromSeed()
var wallet = new SampleWallet("3d977063d3e2ee074f8d6806d1fb73d1b3884d29ab032aa1c7121cfddb0467a99330647652bbe6a244074bccaed63dc08a67286dc1fbf1b8aa36e8aa7bfce909");
Assert.AreEqual("3d977063d3e2ee074f8d6806d1fb73d1b3884d29ab032aa1c7121cfddb0467a99330647652bbe6a244074bccaed63dc08a67286dc1fbf1b8aa36e8aa7bfce909", wallet.BIP39Seed);
}

[Test]
public void Notes()
{
// Mnemonic
Mnemonic mnemonic = new Mnemonic("push wrong tribe amazing again cousin hill belt silent found sketch monitor");

// mnemonic -> seed (hex)
string seed = mnemonic.DeriveSeed().ToHexString();
Assert.AreEqual("3d977063d3e2ee074f8d6806d1fb73d1b3884d29ab032aa1c7121cfddb0467a99330647652bbe6a244074bccaed63dc08a67286dc1fbf1b8aa36e8aa7bfce909", seed);

// Seed (hex) -> Xprv
ExtKey extKey = ExtKey.CreateFromSeed(seed.FromHexToByteArray());
BitcoinExtKey bitcoinExtKey = extKey.GetWif(Network.Main);
string wif = bitcoinExtKey.ToWif();
Assert.AreEqual("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", wif);

// string (xprv...) -> BitcoinExtKey Xprv
BitcoinExtKey bitcoinExtKeyParsed = new BitcoinExtKey(wif, Network.Main);
var parsedWif = bitcoinExtKeyParsed.ToWif();
Assert.AreEqual("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", parsedWif);

// BitcoinExtKey -> string (xprv...)
parsedWif = bitcoinExtKeyParsed.ToString();
Assert.AreEqual("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", parsedWif);

// string (xprv...) -> ExtKey Xprv
ExtKey extKeyParsed = ExtKey.Parse(wif, Network.Main);
wif = extKeyParsed.GetWif(Network.Main).ToWif();
Assert.AreEqual("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", wif);

// ExtKey -> string (xprv...)
var stringHex = extKeyParsed.ToString(Network.Main);
Assert.AreEqual("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", stringHex);

ExtKey extKey1 = ExtKey.CreateFromSeed("3d977063d3e2ee074f8d6806d1fb73d1b3884d29ab032aa1c7121cfddb0467a99330647652bbe6a244074bccaed63dc08a67286dc1fbf1b8aa36e8aa7bfce909".FromHexToByteArray());
ExtKey extKey2 = ExtKey.Parse("xprv9s21ZrQH143K36zpCxiwo4sgScCQm3fef6K8ajZiJ7wseezmwAi6sKhEuWexTKt7CNp1z2KKsMTJhZXrhEKVYbpEHrB9DnsTp31YcWtA1k3", Network.Main);
Assert.AreEqual(extKey1.PrivateKey, extKey2.PrivateKey);
}
}
}
25 changes: 25 additions & 0 deletions test/HDWallet.Ethereum.Tests/GenerateEthereumAddresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,30 @@ public void ShouldGenerateFromPrivateKey()
var walletAddress = ethereumWallet.Address;
Assert.AreEqual(expected: "0xA2ae76fb87C154580a034c116115cE39441Add6F", actual: walletAddress);
}

[Test]
public void ShouldGenerateFromPublickey()
{
IPublicWallet publicWallet = new EthereumPublicWallet("02ba33e9bba01836a3c5c8c4aa70abb16ccffc66470d40def867a0d66fa3d40c27");
Assert.AreEqual(expected: "0xA2ae76fb87C154580a034c116115cE39441Add6F", actual: publicWallet.Address);
}

[Test]
public void ShouldGenerateFromPublickeyBytes()
{
var ethereumWallet = new EthereumWallet("ca5edb4b5db35fc6677d9c6fe223afc3343014653b159ae4291f543a78761c87");

IPublicWallet publicWallet = new EthereumPublicWallet(ethereumWallet.PublicKeyBytes);
Assert.AreEqual(expected: "0xA2ae76fb87C154580a034c116115cE39441Add6F", actual: publicWallet.Address);
}

[Test]
public void ShouldGenerateFromPublickeyBytesAndMatchWithWallet()
{
IWallet ethereumWallet = new EthereumWallet("ca5edb4b5db35fc6677d9c6fe223afc3343014653b159ae4291f543a78761c87");
IPublicWallet publicWallet = new EthereumPublicWallet("02ba33e9bba01836a3c5c8c4aa70abb16ccffc66470d40def867a0d66fa3d40c27");

Assert.AreEqual(expected: ethereumWallet.Address, actual: publicWallet.Address);
}
}
}
Loading

0 comments on commit e0b9e81

Please sign in to comment.