/// <inheritdoc cref="AddNewAccount(string, string, byte[], Network, DateTimeOffset)"/> /// <summary> /// Adds an account to the current account root using extended public key and account index. /// </summary> /// <param name="accountExtPubKey">The extended public key for the account.</param> /// <param name="accountIndex">The zero-based account index.</param> public HdAccount AddNewAccount(ExtPubKey accountExtPubKey, int accountIndex, Network network, DateTimeOffset accountCreationTime) { ICollection <HdAccount> hdAccounts = this.Accounts.ToList(); if (hdAccounts.Any(a => a.Index == accountIndex)) { throw new WalletException("There is already an account in this wallet with index: " + accountIndex); } if (hdAccounts.Any(x => x.ExtendedPubKey == accountExtPubKey.ToString(network))) { throw new WalletException("There is already an account in this wallet with this xpubkey: " + accountExtPubKey.ToString(network)); } string accountHdPath = HdOperations.GetAccountHdPath((int)this.CoinType, accountIndex); var newAccount = new HdAccount { Index = accountIndex, ExtendedPubKey = accountExtPubKey.ToString(network), ExternalAddresses = new List <HdAddress>(), InternalAddresses = new List <HdAddress>(), Name = $"account {accountIndex}", HdPath = accountHdPath, CreationTime = accountCreationTime }; hdAccounts.Add(newAccount); this.Accounts = hdAccounts; return(newAccount); }
/// <summary> /// Adds an account to the current account root using encrypted seed and password. /// </summary> /// <remarks>The name given to the account is of the form "account (i)" by default, where (i) is an incremental index starting at 0. /// According to BIP44, an account at index (i) can only be created when the account at index (i - 1) contains transactions. /// <seealso cref="https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki"/></remarks> /// <param name="password">The password used to decrypt the wallet's encrypted seed.</param> /// <param name="encryptedSeed">The encrypted private key for this wallet.</param> /// <param name="chainCode">The chain code for this wallet.</param> /// <param name="network">The network for which this account will be created.</param> /// <param name="accountCreationTime">Creation time of the account to be created.</param> /// <returns>A new HD account.</returns> public HdAccount AddNewAccount(string password, string encryptedSeed, byte[] chainCode, Network network, DateTimeOffset accountCreationTime) { Guard.NotEmpty(password, nameof(password)); Guard.NotEmpty(encryptedSeed, nameof(encryptedSeed)); Guard.NotNull(chainCode, nameof(chainCode)); int newAccountIndex = 0; ICollection <HdAccount> hdAccounts = this.Accounts.ToList(); if (hdAccounts.Any()) { newAccountIndex = hdAccounts.Max(a => a.Index) + 1; } // Get the extended pub key used to generate addresses for this account. string accountHdPath = HdOperations.GetAccountHdPath((int)this.CoinType, newAccountIndex); Key privateKey = HdOperations.DecryptSeed(encryptedSeed, password, network); ExtPubKey accountExtPubKey = HdOperations.GetExtendedPublicKey(privateKey, chainCode, accountHdPath); var newAccount = new HdAccount { Index = newAccountIndex, ExtendedPubKey = accountExtPubKey.ToString(network), ExternalAddresses = new List <HdAddress>(), InternalAddresses = new List <HdAddress>(), Name = $"account {newAccountIndex}", HdPath = accountHdPath, CreationTime = accountCreationTime }; hdAccounts.Add(newAccount); this.Accounts = hdAccounts; return(newAccount); }
/// <summary> /// Create an account for a specific account index and account name pattern. /// </summary> /// <param name="password">The password used to decrypt the wallet's encrypted seed.</param> /// <param name="encryptedSeed">The encrypted private key for this wallet.</param> /// <param name="chainCode">The chain code for this wallet.</param> /// <param name="network">The network for which this account will be created.</param> /// <param name="accountCreationTime">Creation time of the account to be created.</param> /// <param name="newAccountIndex">The optional account index to use.</param> /// <param name="newAccountName">The optional account name to use.</param> /// <returns>A new HD account.</returns> public HdAccount CreateAccount(string password, string encryptedSeed, byte[] chainCode, Network network, DateTimeOffset accountCreationTime, int newAccountIndex, string newAccountName = null) { if (string.IsNullOrEmpty(newAccountName)) { newAccountName = string.Format(Wallet.AccountNamePattern, newAccountIndex); } // Get the extended pub key used to generate addresses for this account. string accountHdPath = HdOperations.GetAccountHdPath((int)this.CoinType, newAccountIndex); Key privateKey = HdOperations.DecryptSeed(encryptedSeed, password, network); ExtPubKey accountExtPubKey = HdOperations.GetExtendedPublicKey(privateKey, chainCode, accountHdPath); return(new HdAccount { Index = newAccountIndex, ExtendedPubKey = accountExtPubKey.ToString(network), ExternalAddresses = new List <HdAddress>(), InternalAddresses = new List <HdAddress>(), Name = newAccountName, HdPath = accountHdPath, CreationTime = accountCreationTime }); }
public DerivationStrategyBase CreateDerivationStrategy(ExtPubKey pubKey, bool p2sh) { pubKey = pubKey ?? new ExtKey().Neuter(); string suffix = this.RPC.Capabilities.SupportSegwit ? "" : "-[legacy]"; suffix += p2sh ? "-[p2sh]" : ""; return(new DerivationStrategyFactory(this.Network).Parse($"{pubKey.ToString(this.Network)}{suffix}")); }
public void CanRoundTripExtKeyBase58Data() { var key = new ExtKey(); ExtPubKey pubkey = key.Neuter(); Assert.True(ExtKey.Parse(key.ToString(this.networkMain)).ToString(this.networkMain) == key.ToString(this.networkMain)); Assert.True(ExtPubKey.Parse(pubkey.ToString(this.networkMain)).ToString(this.networkMain) == pubkey.ToString(this.networkMain)); }
public DerivationStrategyBase CreateDerivationStrategy(ExtPubKey pubKey, bool p2sh) { key = key ?? new ExtKey(); pubKey = pubKey ?? key.Neuter(); string suffix = this.RPC.Capabilities.SupportSegwit ? "" : "-[legacy]"; suffix += p2sh ? "-[p2sh]" : ""; scriptPubKeyType = p2sh ? ScriptPubKeyType.SegwitP2SH : ScriptPubKeyType.Segwit; return(NBXplorerNetwork.DerivationStrategyFactory.Parse($"{pubKey.ToString(this.Network)}{suffix}")); }
private void btnCalculate_Click(object sender, RoutedEventArgs e) { var functions = new Functions(); wordList = functions.SelectedLanguage(cboLanguage.SelectedValue.ToString()); Mnemonic mnemo = new Mnemonic(txtMnemonic.Text, wordlist: wordList); ExtKey hdRoot = new ExtKey(); ExtKey key32; ExtPubKey pubKey32; hdRoot = mnemo.DeriveExtKey(txtPasswort.Text); txtRoot.Text = hdRoot.ToString(Network.Main); var wallet = new Wallet(txtMnemonic.Text, txtPasswort.Text); txtSeed.Text = wallet.Seed; switch (cboCoin.SelectedValue) { case "BTC - Bitcoin (BIP-44)": ExtKey key44 = hdRoot.Derive(new NBitcoin.KeyPath("m/44'/0'/" + cboAccount.SelectedValue + "'/" + Convert.ToString(cboType.SelectedValue).Substring(0, 1) + "/" + cboIndex.SelectedValue)); ExtPubKey pubKey44 = key44.Neuter(); txtAddress.Text = key44.PrivateKey.PubKey.ToString(Network.Main); txtPublicKey.Text = Convert.ToString(pubKey44.PubKey.ScriptPubKey).Substring(0, 66); txtExtPublicKey.Text = pubKey44.ToString(Network.Main); txtPrivateKey.Text = key44.PrivateKey.ToString(Network.Main); key32 = hdRoot.Derive(new NBitcoin.KeyPath("m/44'/0'/" + cboAccount.SelectedValue + "'/" + Convert.ToString(cboType.SelectedValue).Substring(0, 1))); pubKey32 = key32.Neuter(); txt32ExtPublicKey.Text = pubKey32.ToString(Network.Main); break; case "ETH - Ethereum (BIP-44)": int index = Convert.ToInt32(cboIndex.SelectedValue); var account = wallet.GetAccount(index); txtAddress.Text = account.Address; txtPublicKey.Text = ""; txtExtPublicKey.Text = ""; txtPrivateKey.Text = account.PrivateKey; break; case "BTC - Bitcoin (BIP-49)": ExtKey key49 = hdRoot.Derive(new NBitcoin.KeyPath("m/49'/0'/" + cboAccount.SelectedValue + "'/" + Convert.ToString(cboType.SelectedValue).Substring(0, 1) + "/" + cboIndex.SelectedValue)); ExtPubKey pubKey49 = key49.Neuter(); txtAddress.Text = key49.PrivateKey.PubKey.WitHash.GetAddress(Network.Main).GetScriptAddress().ToString(); txtPublicKey.Text = Convert.ToString(pubKey49.PubKey.ScriptPubKey).Substring(0, 66); txtExtPublicKey.Text = pubKey49.ToString(Network.Main); txtPrivateKey.Text = key49.PrivateKey.ToString(Network.Main); key32 = hdRoot.Derive(new NBitcoin.KeyPath("m/49'/0'/" + cboAccount.SelectedValue + "'/" + Convert.ToString(cboType.SelectedValue).Substring(0, 1))); pubKey32 = key32.Neuter(); txt32ExtPublicKey.Text = pubKey32.ToString(Network.Main); break; } }
public void GeneratePubkey() { var network = Network.RegTest; ExtKey masterKey = new ExtKey(); Console.WriteLine("Master key : " + masterKey.ToString(network)); ExtPubKey masterPubKey = masterKey.Neuter(); ExtPubKey pubkey = masterPubKey.Derive(0); Console.WriteLine("PubKey " + 0 + " : " + pubkey.ToString(network)); }
public void pubKeysDerivedFromExtendedPrivateAndPublicKeysMatch() { string password = "******"; string passphrase = password; string mnemonic = "chalk call anger chase endless level slow sleep coast left sand enter save bind curious puzzle stadium volume mixture shuffle hurry gas borrow believe"; ExtKey extendedKey = HdOperations.GetExtendedKey(mnemonic, passphrase); string encryptedSeed = extendedKey.PrivateKey.GetEncryptedBitcoinSecret(password, this.Network).ToWif(); Key privateKey = HdOperations.DecryptSeed(encryptedSeed, password, this.Network); string accountHdPath = HdOperations.GetAccountHdPath(COINTYPE, 0); string path = HdOperations.CreateHdPath(COINTYPE, 0, false, 0); ExtPubKey accountExtPubKey = HdOperations.GetExtendedPublicKey(privateKey, extendedKey.ChainCode, accountHdPath); var subjectPubKey = HdOperations.GeneratePublicKey(accountExtPubKey.ToString(this.Network), 0, false, this.Network); var subjectPrivKey = HdOperations.GetExtendedPrivateKey(privateKey, extendedKey.ChainCode, path, this.Network); Assert.Equal(subjectPubKey.ScriptPubKey, subjectPrivKey.PrivateKey.PubKey.ScriptPubKey); }
/// <inheritdoc /> public HdAccount CreateNewAccount(Wallet wallet, string password) { Guard.NotNull(wallet, nameof(wallet)); Guard.NotEmpty(password, nameof(password)); // get the accounts for this type of coin var accounts = wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).Accounts.ToList(); int newAccountIndex = 0; if (accounts.Any()) { newAccountIndex = accounts.Max(a => a.Index) + 1; } // get the extended pub key used to generate addresses for this account var privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network); var seedExtKey = new ExtKey(privateKey, wallet.ChainCode); var accountHdPath = $"m/44'/{(int)this.coinType}'/{newAccountIndex}'"; KeyPath keyPath = new KeyPath(accountHdPath); ExtKey accountExtKey = seedExtKey.Derive(keyPath); ExtPubKey accountExtPubKey = accountExtKey.Neuter(); var newAccount = new HdAccount { Index = newAccountIndex, ExtendedPubKey = accountExtPubKey.ToString(wallet.Network), ExternalAddresses = new List <HdAddress>(), InternalAddresses = new List <HdAddress>(), Name = $"account {newAccountIndex}", HdPath = accountHdPath, CreationTime = DateTimeOffset.Now }; accounts.Add(newAccount); wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).Accounts = accounts; return(newAccount); }
static void Test(string [] args) { ExtKey masterKey = new ExtKey(); Console.WriteLine("Master Key: " + masterKey.ToString(Network.Main)); for (int i = 0; i < 7; i++) { ExtKey key = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + key.ToString(Network.Main)); } //Going from Key to ExtKey ExtKey extKey = new ExtKey(); byte[] chainCode = extKey.ChainCode; Key privateKey = extKey.PrivateKey; ExtKey newExtKey = new ExtKey(privateKey, chainCode); //Allowing a third party generate public keys(addresses) without knowing your private key ExtPubKey masterPubKey = masterKey.Neuter(); for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.Main)); } //Testing using one value masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); //Payment server generate pubKey1 ExtPubKey pubKey1 = masterPubKey.Derive((uint)184757585); //You get the private key ExtKey key1 = masterKey.Derive((uint)184757585); //Check if it is legit Console.WriteLine("Generated address : " + pubKey1.PubKey.GetAddress(Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(Network.Main)); //Parent Key and Child Keys ExtKey parentExtKey = new ExtKey(); ExtPubKey parentExtPubKey = parentExtKey.Neuter(); ExtKey childExtKey = parentExtKey.Derive(0); ExtKey childExtKey2 = parentExtKey.Derive(2); ExtKey child2ExtKey = parentExtKey.Derive(1).Derive(2); string childAddress = childExtKey.PrivateKey.PubKey.GetAddress(Network.Main).ToString(); string child2Address = child2ExtKey.PrivateKey.PubKey.GetAddress(Network.Main).ToString(); ExtKey parentKey1Recovered = childExtKey.GetParentExtKey(parentExtPubKey); ExtKey parentKey2Recovered = childExtKey2.GetParentExtKey(parentExtPubKey); //Mnemonic Code for HD Keys Mnemonic mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); ExtKey hdRoot = mnemonic.DeriveExtKey("my password"); var mnemonicStr = mnemonic.ToString(); Console.WriteLine(mnemonic); //If you have the mnemonic and the password you can recover the hdRoot Key mnemonic = new Mnemonic(mnemonicStr, Wordlist.English); hdRoot = mnemonic.DeriveExtKey("my password"); Console.ReadKey(); }
private static void Main() { RandomUtils.Random = new UnsecureRandom(); //========================================================================================== //Chapter. HD Wallet(BIP 32) //Let’s keep in mind the problems that we want to resolve: //1.Prevent outdated backups //2.Delegating key / address generation to an untrusted peer //A “Deterministic” wallet would fix our backup problem. With such a wallet, you would have to save only the seed.From this seed, you can generate the same series of private keys over and over. //This is what the “Deterministic” stands for. //As you can see, from the master key, I can generate new keys: //Create a masterKey. ExtKey masterKey = new ExtKey(); Console.WriteLine("Master key : " + masterKey.ToString(Network.Main)); //Output: //xprv9s21ZrQH143K46gx2C5V4o9iEn52h9y2Y7LykXSTVPFLM28kyMuj8BkicTd3uAiqrVPxb3BZ4fVkzKDwzxVpkEhsqS5HD6vrGDf5D613Lwt //Create 5 derived keys based on the masterKey. for (int i = 0; i < 5; i++) { ExtKey key = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + key.ToString(Network.Main)); } //Output: //Key 0 : xprv9uvuViKQnT4vo8rSWEtXYXxDLMvt4SrSToHVs3ZpzKXpHd5deZNDKj57XnrQ8rRZSANm3oZBjXN245oy161otLGddxch46UrtTH3tB5fMKQ //Key 1 : xprv9uvuViKQnT4vqG7czoZ1VYGPmK9yhXBLGYKgZpiB271L6ekwbzyPo9fHE3F4cAay6qbwczt1K35cEF3HrykPGR9agzmHNhBPDSZtyoxhMbh //Key 2 : xprv9uvuViKQnT4vs7GpRKcouP8TP8ERksBG5GdNmocVDp6R8nHGTYH4qwG8PY75hHGKsTKQdQmRAiQnZim6UuYguiaUx1dpqGXafM1Lr1WEkzy //Key 3 : xprv9uvuViKQnT4vwDWmEvSEae5nLGbKLMmeMLLCc5MDD9iVSDhDqNpzSErHBYm1TRZGfDM2YbHZyCtmas1d2JRSWMYLKbJMGhyB9ynSbyLTh14 //Key 4 : xprv9uvuViKQnT4vxgXFvxk6FhKYgzU8NgW3xMfqciveTWKpJDkdCmQiJjAdGUij7QckJ1ZBqKuY1RSyw9JeGM7P6cTMPZqVZBu9SCAfHEpp8SS //You only need to save the masterKey, since you can generate the same suite of private keys over and over. //As you can see, these keys are ExtKey and not Key as you are used to. However, this should not stop you since you have the real private key inside of these keys: //You can go back from a Key to an ExtKey by supplying the Key and the ChainCode to the ExtKey constructor. This works as follows: //Create extKey. ExtKey extKey = new ExtKey(); //Get ChainCode from extKey. byte[] chainCode = extKey.ChainCode; //Get a PrivateKey from extKey. Key privateKeyFromExtKey = extKey.PrivateKey; //Supply a private key and ChainCode to the ExtKey constructor to go back from a Key to an ExtKey. ExtKey newExtKey = new ExtKey(privateKeyFromExtKey, chainCode); //The Base58 type equivalent of ExtKey is called BitcoinExtKey. //But how can we solve our second problem: Delegating address creation to a peer that can potentially be hacked like a payment server? //The trick is that you can “neuter” your master key, then you have a public (without private key) version of the master key.From this neutered version, a third party can generate public keys without knowing the private key. //Neuter the master key, then you get a master public key. ExtPubKey masterPubKey = masterKey.Neuter(); //Genarate 5 derived public keys from the master public key. for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.Main)); } //So imagine that your payment server generates pubkey1, and then you can get the corresponding private key with your private master key. masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); //The payment server generates pubkey1. ExtPubKey pubkey1 = masterPubKey.Derive((uint)1); //You get the private key of pubkey1 ExtKey key1 = masterKey.Derive((uint)1); //Check if it is legit Console.WriteLine("Generated address : " + pubkey1.PubKey.GetAddress(Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(Network.Main)); //Generated address: 1Jy8nALZNqpf4rFN9TWG2qXapZUBvquFfX //Expected address: 1Jy8nALZNqpf4rFN9TWG2qXapZUBvquFfX //ExtPubKey is similar to ExtKey except that it holds a public key and not a private key. //Now we have seen how Deterministic keys solve our problems, let’s speak about what the “hierarchical” is for. //In the previous exercise, we have seen that by combining master key + index we could generate another key.We call this process Derivation, the master key is the parent key, and any generated keys are called child keys. //However, you can also derivate children from the child key.This is what the “hierarchical” stands for. //This is why conceptually more generally you can say: Parent Key + KeyPath => Child Key //Now that we have seen how Deterministic keys solve our problems, let’s speak about what the “hierarchical” is for. //In the previous exercise, we have seen that we could generate another derived keys based on a master key by invoking Derive() method on the master key passing integer numbers into an argument. //We call this process a Derivation. And in this scheme, a master key is a parent key, and any generated keys based on the master key are called child keys. //However, I can also derivate children from the "child key". This is what the “Hierarchical” stands for. //This is why conceptually more generally you can say: Parent Key + KeyPath => Child Key //Just suppose the scenario that there is a parent key, "Parent". //And there can be child keys derived from "Parent". => Child(1),Child(2),Child(3),Child(4). //And there can be child keys derived from Child(1). => Child(1, 1), Child(1, 2). //In this diagram, you can derivate Child(1,1) from a parent in two different way: //First generate a parent key. ExtKey parent = new ExtKey(); ExtKey child11ByFirstWay = parent.Derive(1).Derive(1); Console.WriteLine(child11ByFirstWay); //Or above code can be expressed in this way, resulting in an identical output. ExtKey child11BySecondWay = parent.Derive(new KeyPath("1/1")); Console.WriteLine(child11BySecondWay); //Remember that Ancestor ExtKey + KeyPath = Child ExtKey. //This process works indenticaly for ExtPubKey. //Why do you need hierarchical keys? It's because it might be a nice way to classify the type of my keys for multiple accounts. This point is more neat than on BIP44. It also permits segmenting account rights across an organization. //Imagine you are the CEO of a company. You want control over all wallets, but you don’t want the Accounting department to spend the money from the Marketing department. //So, for implementing this constraint, your first idea would be to generate one hierarchy for each department. //CEO Key(the master key)-> derived child Keys from a master key(the CEO key) : Marketing(0), Accounting(0). //Marketing(0)->Child Keys:Marketing(0, 1), Marketing(0, 2). //Accounting(0)->Child Keys:Accounting(0, 2), Accounting(0, 2). //However, in such a case, Accounting and Marketing would be able to recover the CEO’s private key, because we defined such child keys as non-hardened. //Parent ExtPubKey + Child ExtKey(non hardened) => Parent ExtKey. ExtKey ceoKey = new ExtKey(); Console.WriteLine("CEO: " + ceoKey.ToString(Network.Main)); //Note the hardened is false. ExtKey accountingKey = ceoKey.Derive(0, hardened: false); ExtPubKey ceoPubkey = ceoKey.Neuter(); //Recover the CEO key by the accounting private key and the CEO public key. ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubkey); Console.WriteLine("CEO recovered: " + ceoKeyRecovered.ToString(Network.Main)); //CEO: xprv9s21ZrQH143K2XcJU89thgkBehaMqvcj4A6JFxwPs6ZzGYHYT8dTchd87TC4NHSwvDuexuFVFpYaAt3gztYtZyXmy2hCVyVyxumdxfDBpoC //CEO recovered: xprv9s21ZrQH143K2XcJU89thgkBehaMqvcj4A6JFxwPs6ZzGYHYT8dTchd87TC4NHSwvDuexuFVFpYaAt3gztYtZyXmy2hCVyVyxumdxfDBpoC //In other words, a non-hardened key can “climb” the hierarchy. Non-hardened keys should only be used for categorizing accounts that belong to a point of single control. //So in our case, the CEO should create child keys as hardened ones, so the accounting department will not be able to climb the hierarchy. //Identical process to above code except for hardened is true. Console.WriteLine("CEO: " + ceoKey.ToString(Network.Main)); ExtKey accountingKeyHardened = ceoKey.Derive(0, hardened: true); Console.WriteLine(accountingKeyHardened); //Generate derived child accountKeys from ceoKey //However, since accountKeys are hardened, they can't climb hierarchy towards ceoKey. ExtPubKey ceoPubkeyToTestForHardened = ceoKey.Neuter(); Console.WriteLine(ceoPubkeyToTestForHardened); ////At this point, it'll be crashed by this climbing attempt. //ExtKey ceoKeyRecovered = accountingKeyHardened.GetParentExtKey(ceoPubkeyToTestForHardened); //You can also create hardened keys via the ExtKey.Derivate(KeyPath), by using an apostrophe(') after a child’s index such as "1/2/3'" var nonHardenedChildKey = new KeyPath("1/2/3"); var hardenedChildKey = new KeyPath("1/2/3'"); Console.WriteLine(nonHardenedChildKey); Console.WriteLine(hardenedChildKey); //So let’s imagine that the Accounting Department generates 1 parent key for each customer, and a child for each of the customer’s payments. //As the CEO, you want to spend the money on one of these addresses.Here is how you would proceed. ceoKey = new ExtKey(); //Child key is generated as hardened, so it can't climb hierarchy upwards to a parent key. string accounting = "1'"; int customerId = 5; int paymentId = 50; KeyPath path = new KeyPath(accounting + "/" + customerId + "/" + paymentId); //Path will be "1'/5/50" ExtKey paymentKey = ceoKey.Derive(path); Console.WriteLine(paymentKey); //=========================================================================================== //Chapter. Mnemonic Code for HD Keys (BIP39) //As you have seen, generating HD keys is easy.However, what if we want an easy way to transmit such a key by telephone or hand writing? //Cold wallets like Trezor, generate the HD Keys from a sentence that can easily be written down. They call such a sentence “the seed” or “mnemonic”. And it can eventually be protected by a password or a PIN. //The language that you use to generate your 'easy to write' sentence is called a Wordlist. //Wordlist + mnemonic + password => HD Root key. Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); ExtKey hdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(mnemo); Console.WriteLine(hdRoot); //Now, if you have the mnemonic and the password, you can recover the hdRoot key. mnemo = new Mnemonic("minute put grant neglect anxiety case globe win famous correct turn link", Wordlist.English); ExtKey hdRoot1 = mnemo.DeriveExtKey("my password"); Console.WriteLine(hdRoot1); //Currently supported languages for wordlist are, English, Japanese, Spanish, Chinese (simplified and traditional). }
private static void Main() { ExtKey masterKey = new ExtKey(); Console.WriteLine("Master key : " + masterKey.ToString(Network.Main)); for (int i = 0; i < 5; i++) { ExtKey key = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + key.ToString(Network.Main)); } ExtPubKey masterPubKey = masterKey.Neuter(); for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.Main)); } masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); //The payment server generate pubkey1 ExtPubKey pubkey1 = masterPubKey.Derive((uint)1); //You get the private key of pubkey1 ExtKey key1 = masterKey.Derive((uint)1); //Check it is legit Console.WriteLine("Generated address : " + pubkey1.PubKey.GetAddress(Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(Network.Main)); ExtKey parent = new ExtKey(); ExtKey child11 = parent.Derive(1).Derive(1); // OR parent = new ExtKey(); child11 = parent.Derive(new KeyPath("1/1")); ExtKey ceoKey = new ExtKey(); Console.WriteLine("CEO: " + ceoKey.ToString(Network.Main)); ExtKey accountingKey = ceoKey.Derive(0, hardened: true); ExtPubKey ceoPubkey = ceoKey.Neuter(); //ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubkey); //Crash var nonHardened = new KeyPath("1/2/3"); var hardened = new KeyPath("1/2/3'"); ceoKey = new ExtKey(); string accounting = "1'"; int customerId = 5; int paymentId = 50; KeyPath path = new KeyPath(accounting + "/" + customerId + "/" + paymentId); //Path : "1'/5/50" ExtKey paymentKey = ceoKey.Derive(path); Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); ExtKey hdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(mnemo); mnemo = new Mnemonic("minute put grant neglect anxiety case globe win famous correct turn link", Wordlist.English); hdRoot = mnemo.DeriveExtKey("my password"); Console.ReadLine(); }
public static void Execute() { // HD Wallet (BIP 32) // from the master key, I can generate new keys: ExtKey masterKey = new ExtKey(); Console.WriteLine("---------------------HD Wallet (BIP 32)---------------------"); Console.WriteLine("Master key : " + masterKey.ToString(Network.Main)); for (int i = 0; i < 5; i++) { ExtKey childKey = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + childKey.ToString(Network.Main)); } // go back from a Key to an ExtKey by supplying the Key and the ChainCode ExtKey extKey = new ExtKey(); Key key = extKey.PrivateKey; byte[] chainCode = extKey.ChainCode; ExtKey newExtKey = new ExtKey(key, chainCode); Console.WriteLine("extKey==newExtKey: " + (extKey.ToString(Network.Main) == newExtKey.ToString(Network.Main))); // delegating address creation to a peer // third party can generate public keys without knowing the private key ExtPubKey masterPubKey = masterKey.Neuter(); Console.WriteLine("Master Pubkey : " + masterPubKey.ToString(Network.Main)); for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.Main)); } // payment server generates pubkey1 // we can get the corresponding private key with our private master key masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); //The payment server generate pubkey1 ExtPubKey pubkey1 = masterPubKey.Derive((uint)1); //You get the private key of pubkey1 ExtKey key1 = masterKey.Derive((uint)1); //Check it is legit Console.WriteLine("Generated address : " + pubkey1.PubKey.GetAddress(Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(Network.Main)); // Generated address : 1Jy8nALZNqpf4rFN9TWG2qXapZUBvquFfX // Expected address: 1Jy8nALZNqpf4rFN9TWG2qXapZUBvquFfX // “hierarchical”: Parent Key + KeyPath => Child Key (and so on..) // derivate Child(1,1) from parent in two different way ExtKey parent = new ExtKey(); ExtKey child1 = parent.Derive(1); ExtKey child11; child11 = child1.Derive(1); //Or child11 = parent.Derive(1).Derive(1); //Or child11 = parent.Derive(new KeyPath("1/1")); // generate one hierarchy for each department. // non-hardened key, accounting department can “climb” the hierarchy // hardened key, so the marketing department will not be able to climb the hierarchy. ExtKey ceoKey = new ExtKey(); ExtKey accountingKey = ceoKey.Derive(0, hardened: false); ExtKey marketingKey = ceoKey.Derive(0, hardened: true); ExtPubKey ceoPubkey = ceoKey.Neuter(); //Recover ceo key with accounting private key and ceo public key ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubkey); ExtKey ceoKeyNotRecovered = marketingKey.GetParentExtKey(ceoPubkey); //Crash Console.WriteLine(); Console.WriteLine("CEO Key: " + ceoKey.ToString(Network.Main)); Console.WriteLine("CEO recovered: " + ceoKeyRecovered.ToString(Network.Main)); Console.WriteLine("CEO Not recovered: " + ceoKeyNotRecovered.ToString(Network.Main)); //Or KeyPath nonHardened = new KeyPath("1/2/3"); KeyPath hardened = new KeyPath("1/2/3'"); ceoKey = new ExtKey(); string accounting = "1'"; int customerId = 5; int paymentId = 50; KeyPath path = new KeyPath(accounting + "/" + customerId + "/" + paymentId); ExtKey paymentKey = ceoKey.Derive(path); //Path : "1'/5/50" }
private static void KeyGenerationAndEncryption() { // 02.01. Key Generation RandomUtils.AddEntropy("hello"); RandomUtils.AddEntropy(new byte[] { 1, 2, 3 }); var nsaProofKey = new Key(); Console.WriteLine("Key with entropy " + nsaProofKey.GetBitcoinSecret(Network.TestNet)); var derived = SCrypt.BitcoinComputeDerivedKey("hello", new byte[] { 1, 2, 3 }); RandomUtils.AddEntropy(derived); var privateKey = new Key(); var privateKeyBitcoinSecret = privateKey.GetWif(Network.TestNet); Console.WriteLine(privateKeyBitcoinSecret); // L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r BitcoinEncryptedSecret bitcoinEncryptedSecret = privateKeyBitcoinSecret.Encrypt("password"); Console.WriteLine(bitcoinEncryptedSecret); // 6PYKYQQgx947Be41aHGypBhK6TA5Xhi9TdPBkatV3fHbbKrdDoBoXFCyLK var decryptedBitcoinPrivateKey = bitcoinEncryptedSecret.GetSecret("password"); Console.WriteLine(decryptedBitcoinPrivateKey); // L1tZPQt7HHj5V49YtYAMSbAmwN9zRjajgXQt9gGtXhNZbcwbZk2r //02.02 BIP38 and HD (Hierarchical Deterministic) Wallet var passphraseCode = new BitcoinPassphraseCode("my secret", Network.TestNet, null); // we give this passphraseCode to 3rd party EncryptedKeyResult encryptedKeyResult = passphraseCode.GenerateEncryptedSecret(); var generatedAddress = encryptedKeyResult.GeneratedAddress; var encryptedKeySecretEc = encryptedKeyResult.EncryptedKey; var confirmationCode = encryptedKeyResult.ConfirmationCode; Console.WriteLine(confirmationCode.Check("my secret", generatedAddress)); // True var privateKeyBitcoinSecret1 = encryptedKeySecretEc.GetSecret("my secret"); Console.WriteLine(privateKeyBitcoinSecret1.GetAddress() == generatedAddress); // True Console.WriteLine(privateKeyBitcoinSecret1); // KzzHhrkr39a7upeqHzYNNeJuaf1SVDBpxdFDuMvFKbFhcBytDF1R ExtKey masterKey = new ExtKey(); Console.WriteLine("Master key : " + masterKey.ToString(Network.TestNet)); for (int i = 0; i < 5; i++) { ExtKey extKey1 = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + extKey1.ToString(Network.TestNet)); } ExtKey extKey = new ExtKey(); byte[] chainCode = extKey.ChainCode; Key key = extKey.PrivateKey; ExtKey newExtKey = new ExtKey(key, chainCode); ExtPubKey masterPubKey = masterKey.Neuter(); for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.TestNet)); } ExtKey key1 = masterKey.Derive(4); //Check it is legit var generatedAddr = masterPubKey.Derive(4).PubKey.GetAddress(Network.TestNet); var expectedAddr = key1.PrivateKey.PubKey.GetAddress(Network.TestNet); Console.WriteLine("Generated address : " + generatedAddr); Console.WriteLine("Expected address : " + expectedAddr); ExtKey parent = new ExtKey(); ExtKey child11 = parent.Derive(1).Derive(1); ExtKey sameChild11 = parent.Derive(new KeyPath("1/1")); Console.WriteLine("child11 : " + child11.PrivateKey.PubKey.GetAddress(Network.TestNet)); Console.WriteLine("child11.PrivateKey == sameChild11.PrivateKey : " + (child11.PrivateKey == sameChild11.PrivateKey)); ExtKey ceoKey = new ExtKey(); Console.WriteLine("CEO: " + ceoKey.ToString(Network.TestNet)); ExtKey notHardenedAccountingKey = ceoKey.Derive(0, hardened: false); ExtPubKey ceoPubkey = ceoKey.Neuter(); //Recover ceo key with accounting private key and ceo public key ExtKey ceoKeyRecovered = notHardenedAccountingKey.GetParentExtKey(ceoPubkey); Console.WriteLine("CEO recovered: " + ceoKeyRecovered.ToString(Network.TestNet)); ExtKey hardenedAccountingKey = ceoKey.Derive(0, hardened: true); // ExtKey ceoKeyRecovered2 = hardenedAccountingKey.GetParentExtKey(ceoPubkey); => //throws exception Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); // generate random 12 words list ExtKey hdRoot = mnemo.DeriveExtKey("my password"); mnemo = new Mnemonic("minute put grant neglect anxiety case globe win famous correct turn link", Wordlist.English); hdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(mnemo); Console.WriteLine(hdRoot.ToString(Network.TestNet)); var scanKey = new Key(); var spendKey = new Key(); BitcoinStealthAddress stealthAddress = new BitcoinStealthAddress ( scanKey: scanKey.PubKey, pubKeys: new[] { spendKey.PubKey }, signatureCount: 1, bitfield: null, network: Network.TestNet); Transaction transaction = new Transaction(); stealthAddress.SendTo(transaction, Money.Coins(1.0m)); Console.WriteLine(transaction); // personal tests Mycelium mnemo = new Mnemonic("artist tiger always access sport major donkey coil scale carry laptop ticket", Wordlist.English); hdRoot = mnemo.DeriveExtKey();//leave the password null as sample Console.WriteLine(hdRoot.ToString(Network.Main)); var hardened2 = new KeyPath("44'/0'/0'/0/1"); ExtKey paymentKey2 = hdRoot.Derive(hardened2); Console.WriteLine(hardened2 + ": " + paymentKey2.ScriptPubKey.GetDestinationAddress(Network.Main)); Console.WriteLine(hardened2 + ": private " + paymentKey2.ToString(Network.Main)); var hardened1 = new KeyPath("44'/0'/0'/0/0"); ExtKey paymentKey1 = hdRoot.Derive(hardened1); Console.WriteLine(hardened1 + ": " + paymentKey1.ScriptPubKey.GetDestinationAddress(Network.Main)); Console.WriteLine(hardened1 + ": private " + paymentKey1.ToString(Network.Main)); }
public static void HDWallets() { // Deterministic wallet means you only have to save the seed. // From the seed, you can generate the same series of private keys over and over. ExtKey masterKey = new ExtKey(); Console.WriteLine("Master key : " + masterKey.ToString(Network.Main)); for (int i = 0; i < 5; ++i) { ExtKey key = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + key.ToString(Network.Main)); } // You can go back from a Key to an ExtKey by supplying the Key and the ChainCode to the ExtKey constructor. ExtKey extKey = new ExtKey(); byte[] chainCode = extKey.ChainCode; Key key = extKey.PrivateKey; ExtKey newExtKey = new ExtKey(key, chainCode); // BitcoinExtKey is a base58 type equivalent // You can get a Public version of the master key and generate public keys without knowing the private. ExtPubKey masterPubKey = masterKey.Neuter(); for (int i = 0; i < 5; ++i) { ExtPubKey pubKey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubKey.ToString(Network.Main)); } // Then you can get the corresponding private keys with the private master key. ExtPubKey pubKey1 = masterPubKey.Derive((uint)1); ExtKey key1 = masterKey.Derive((uint)1); // Check its legitimacy Console.WriteLine("Generated address : " + pubKey1.PubKey.GetAddress(Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(Network.Main)); // However, the hierarchical side of the wallet comes in when you start deriving grandchild keys from the child keys. ExtKey key12 = key1.Derive((uint)2); // Or ExtKey key11 = masterKey.Derive(new KeyPath("1/1")); // Hierarchical keys are a nice way to classify the type of your keys for multiple accounts. // You can also securely separate off different people within these specific classifications. // However, you can normally get the master private key from the master public key and child private key ExtKey ceoKey = new ExtKey(); Console.WriteLine("CEO: " + ceoKey.ToString(Network.Main)); ExtKey accountingKey = ceoKey.Derive(0, false); ExtPubKey ceoPubKey = ceoKey.Neuter(); ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubKey); Console.WriteLine("CEO recovered: " + ceoKeyRecovered.ToString(Network.Main)); // In other words, it's a 2-way path for non-hardened keys. // Non-hardened keys should only be used for categorizing accounts that belong to a point of single control. // If you try to recover a parent's private key with a hardened child key, the program will crash. // You can also harden a childKey with the KeyPath by using an apostrophe after the child's index. var nonHardened = new KeyPath("1/2/3"); var hardened = new KeyPath("1/2/3'"); // Let's imagine that Accounting generates 1 parent key for each customer and a child for each payment. string accounting = "1'"; int custoemrId = 5; int paymentId = 50; KeyPath path = new KeyPath(accounting + "/" + custoemrId + "/" + paymentId); // Path: "1'/5/50" ExtKey paymentKey = ceoKey.Derive(path); // Cold wallets like Trezor generator the HD Keys from a sentence that can easily be written down. // This sentence is referred to as "the seed" or "mnemonic". // It can eventually be protected by a password or a PIN. // The language that you use to generate your 'easy to write' sentence is called a Wordlist. Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); ExtKey hdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(mnemo); Mnemonic mnemoRecovered = new Mnemonic("minute put grant neglect anxiety case globe win famous correct turn link", Wordlist.English); ExtKey hdRootRecovered = mnemo.DeriveExtKey("my password"); }
public string GetMasterPublicKey() { return(_masterPubKey.ToString(network)); }
public static void CreateWallet() { var secret = new BitcoinSecret("L3E7oUgKrmF4ED4xTSKem3NjduRcCVNmfG59wTG3p28YGkY9a5og"); ExtKey masterKey = new ExtKey(); Transaction tx; var input = new TxIn(); //input.PrevOut = new OutPoint(new uint256("1CxZnG2Mb31YHcsoKx18yshWdbcM9Y1mb5"),0); Coin y = new Coin(); y.Amount = 5; //ColoredTransaction coloredTransaction = new ColoredTransaction(); Console.WriteLine("Address: " + secret.PubKey.GetAddress(Network.Main)); Console.WriteLine("Key: " + masterKey.GetWif(Network.Main)); Console.WriteLine("Master key : " + masterKey.ToString(Network.Main)); for (int i = 0; i < 5; i++) { ExtKey key2 = masterKey.Derive((uint)i); Console.WriteLine("Key " + i + " : " + key2.ToString(Network.Main)); } ExtKey extKey = new ExtKey(); byte[] chainCode = extKey.ChainCode; Key key = extKey.PrivateKey; ExtKey newExtKey = new ExtKey(key, chainCode); ExtPubKey masterPubKey = masterKey.Neuter(); for (int i = 0; i < 5; i++) { ExtPubKey pubkey = masterPubKey.Derive((uint)i); Console.WriteLine("PubKey " + i + " : " + pubkey.ToString(Network.Main)); } masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); ExtPubKey pubkey1 = masterPubKey.Derive((uint)1); ExtKey key1 = masterKey.Derive((uint)1); Console.WriteLine("Generated address : " + pubkey1.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)); Console.WriteLine("Expected address : " + key1.PrivateKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)); ExtKey parent = new ExtKey(); ExtKey child11 = parent.Derive(new KeyPath("1/1")); ExtKey Key = new ExtKey(); Console.WriteLine("Key: " + Key.ToString(Network.Main)); ExtKey accountingKey = Key.Derive(0, hardened: false); ExtPubKey ceoPubkey = Key.Neuter(); ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubkey); Console.WriteLine("Key recovered: " + ceoKeyRecovered.ToString(Network.Main)); var nonHardened = new KeyPath("1/2/3"); var hardened = new KeyPath("1/2/3'"); Key = new ExtKey(); string accounting = "1'"; int customerId = 5; int paymentId = 50; KeyPath path = new KeyPath(accounting + "/" + customerId + "/" + paymentId); ExtKey paymentKey = Key.Derive(path); }
private static DirectDerivationStrategy CreateDerivationStrategy(ExtPubKey pubKey = null) { pubKey = pubKey ?? new ExtKey().Neuter(); return((DirectDerivationStrategy) new DerivationStrategyFactory(Network.RegTest).Parse($"{pubKey.ToString(Network.RegTest)}-[legacy]")); }
private static P2SHDerivationStrategy CreateP2SHDerivationStrategy(ExtPubKey pubKey = null) { pubKey = pubKey ?? new ExtKey().Neuter(); return((P2SHDerivationStrategy) new DerivationStrategyFactory(Network.RegTest).Parse($"{pubKey.ToString(Network.RegTest)}-[p2sh]")); }
public ClientDestinationWallet(BitcoinExtPubKey extPubKey, KeyPath derivationPath, IRepository repository, Network network) { if (derivationPath == null) { throw new ArgumentNullException("derivationPath"); } if (extPubKey == null) { throw new ArgumentNullException("extPubKey"); } if (repository == null) { throw new ArgumentNullException("repository"); } if (network == null) { throw new ArgumentNullException("network"); } _Network = network; _Repository = repository; _ExtPubKey = extPubKey.ExtPubKey.Derive(derivationPath); _DerivationPath = derivationPath; _WalletId = "Wallet_" + Encoders.Base58.EncodeData(Hashes.Hash160(Encoding.UTF8.GetBytes(_ExtPubKey.ToString() + "-" + derivationPath.ToString())).ToBytes()); }
private static void Main(string[] args) { // add entropy before creating key RandomUtils.AddEntropy("hello"); RandomUtils.AddEntropy(new byte[] { 1, 2, 3 }); // key created with added entropy var nsaProofKey = new Key(); // What NBitcoin does when you call AddEntropy(data) is: // additionalEntropy = SHA(SHA(data) ^ additionalEntropy) // Then when you generate a new number: // result = SHA(PRNG() ^ additionalEntropy) // Key Derivation Function is a way to have a stronger key, even if your entropy is low // KDF is a hash function that wastes computing resources on purpose. var derived = SCrypt.BitcoinComputeDerivedKey("hello", new byte[] { 1, 2, 3 }); RandomUtils.AddEntropy(derived); // even if attacker knows that your source of entropy contains 5 letters, // they will need to run Scrypt to check each possibility // standard for encrypting private key with a password using kdf is BIP38 var privateKey = new Key(); var bitcoinPrivateKey = privateKey.GetWif(Network.Main); Console.WriteLine(bitcoinPrivateKey); BitcoinEncryptedSecret encryptedBitcoinPrivateKey = bitcoinPrivateKey.Encrypt("password"); Console.WriteLine(encryptedBitcoinPrivateKey); var decryptedBitcoinPrivateKey = encryptedBitcoinPrivateKey.GetKey("password"); Console.WriteLine(decryptedBitcoinPrivateKey); Key keyFromIncorrectPassword = null; Exception error = null; try { keyFromIncorrectPassword = encryptedBitcoinPrivateKey.GetKey("lahsdlahsdlakhslkdash"); } catch (Exception e) { error = e; } var result = keyFromIncorrectPassword != null ? keyFromIncorrectPassword.ToString() : $"{error?.GetType().Name ?? "Error"}: icorrect password"; Console.WriteLine(result); // BIP 38 // how to delegate Key and Address creation to an untrusted peer // create pass phrase code BitcoinPassphraseCode passphraseCode = new BitcoinPassphraseCode("my secret", Network.Main, null); Console.WriteLine(passphraseCode); // then give passPhraseCode to 3rd party key generator // third party can generate encrypted keys on your behalf // without knowing your password and private key. EncryptedKeyResult encryptedKeyResult = passphraseCode.GenerateEncryptedSecret(); var generatedAddress = encryptedKeyResult.GeneratedAddress; var encryptedKey = encryptedKeyResult.EncryptedKey; // used by 3rd party to confirm generated key and address correspond to password var confirmationCode = encryptedKeyResult.ConfirmationCode; // check var isValid = confirmationCode.Check("my secret", generatedAddress); Console.WriteLine(isValid); var bitcoinPrivateKeyFromPassphraseCode = encryptedKey.GetSecret("my secret"); Console.WriteLine(bitcoinPrivateKeyFromPassphraseCode.GetAddress() == generatedAddress); Console.WriteLine(bitcoinPrivateKey); // BIP 32 // hierarchical deterministic wallets // prevent outdated backups var masterKey = new ExtKey(); Console.WriteLine($"Master Key: {masterKey.ToString(Network.Main)}"); for (uint i = 0; i < 5; i++) { ExtKey nextKey = masterKey.Derive(i); Console.WriteLine($"Key {i} : {nextKey.ToString(Network.Main)}"); } // go back from a Key to ExtKey by supplying the Key and the ChainCode to the ExtKey constructor. var extKey = new ExtKey(); byte[] chainCode = extKey.ChainCode; Key key = extKey.PrivateKey; var newExtKey = new ExtKey(key, chainCode); // the base58 type equivalent of ExtKey is BitcoinExtKey // "neuter" master key so 3rd party can generate public keys without knowing private key ExtPubKey masterPubKey = masterKey.Neuter(); for (uint i = 0; i < 5; i++) { ExtPubKey pubKey = masterPubKey.Derive(i); Console.WriteLine($"PubKey {i} : {pubKey.ToString(Network.Main)}"); } // get corresponding private key with master key masterKey = new ExtKey(); masterPubKey = masterKey.Neuter(); // 3rd party generates pubkey1 ExtPubKey pubKey1 = masterPubKey.Derive(1); // get privatekey of pubKey1 ExtKey key1 = masterKey.Derive(1); Console.WriteLine($"Generated address: {pubKey1.PubKey.GetAddress(Network.Main)}"); Console.WriteLine($"Expected address: {key1.PrivateKey.PubKey.GetAddress(Network.Main)}"); // deterministic keys // derive the 1st child of the 1st child ExtKey parent = new ExtKey(); // method 1: ExtKey child11 = parent.Derive(1).Derive(1); // method 2: child11 = parent.Derive(new KeyPath("1/1")); // why use HD wallets ? // easier control, easier to classify keys for multiple accounts // but child keys can recover parent key (non-hardened) ExtKey ceoExtKey = new ExtKey(); Console.WriteLine($"CEO: {ceoExtKey.ToString(Network.Main)}"); ExtKey accountingKey = ceoExtKey.Derive(0, hardened: false); ExtPubKey ceoPubKey = ceoExtKey.Neuter(); // recover ceo key with accounting private key and ceo public key ExtKey ceoKeyRecovered = accountingKey.GetParentExtKey(ceoPubKey); Console.WriteLine($"CEO recovered: {ceoKeyRecovered.ToString(Network.Main)}"); // create a hardened key var privateCeoExtKey = new ExtKey(); Console.WriteLine($"Private CEO: {privateCeoExtKey.ToString(Network.Main)}"); var assitantExtKey = privateCeoExtKey.Derive(1, hardened: true); ExtPubKey privateCeoPubKey = privateCeoExtKey.Neuter(); ExtKey privateCeoKeyRecovered = null; try { privateCeoKeyRecovered = assitantExtKey.GetParentExtKey(privateCeoPubKey); } catch (Exception e) { Console.WriteLine($"{e.Message}"); } // creating hardened keys via keypath var isNonHardened = new KeyPath("1/2/3").IsHardened; Console.WriteLine(isNonHardened); var isHardened = new KeyPath("1/2/3'").IsHardened; Console.WriteLine(isHardened); // imagine that the Accounting Department generates 1 parent key for each customer, and a child for each of the customer’s payments. // As the CEO, you want to spend the money on one of these addresses. var accountingCeoKey = new ExtKey(); string accounting = "1'"; // hardened with apostrophe int customerId = 5; int paymentId = 50; KeyPath path = new KeyPath($"{accounting}/{customerId}/{paymentId}"); // path: 1/5/50 ExtKey paymentKey = accountingCeoKey.Derive(path); Console.WriteLine(paymentKey); // mnemonic code for HD keys BIP 39 // used for easy to write keys Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve); ExtKey hdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(mnemo); // recover hdRoot with mnemonic and password mnemo = new Mnemonic(mnemo.ToString(), Wordlist.English); ExtKey recoverdHdRoot = mnemo.DeriveExtKey("my password"); Console.WriteLine(hdRoot.PrivateKey == recoverdHdRoot.PrivateKey); // dark wallet // Prevent outdated backups // Delegate key / address generation to an untrusted peer // bonus feature: only share one address (StealthAddress) var scanKey = new Key(); var spendKey = new Key(); var stealthAddress = new BitcoinStealthAddress( scanKey: scanKey.PubKey, pubKeys: new[] { spendKey.PubKey }, signatureCount: 1, bitfield: null, network: Network.Main ); // payer will take StealthAddress and generate temp key called Ephem Key, and generate a Stealth Pub Key // then they package the Ephem PubKey in Stealth Metadata obj embedded in OP_RETURN // they will also add the output to the generated bitcoin address (Stealth pub key) //The creation of the EphemKey is an implementation detail // it can be omitted as NBitcoin will generate one automatically: var ephemKey = new Key(); var transaction = new Transaction(); stealthAddress.SendTo(transaction, Money.Coins(1.0m), ephemKey); Console.WriteLine(transaction); // the Scanner knows the StealthAddress // and recovers the Stealth PubKey and Bitcoin Address with the Scan Key // scanner then checks if if one of the tx corresponds to the address // if true, Scanner notifies the Receiver about tx // the receiver can get the private key of the address with their spend key // note: a StealthAddress can have mulitple spend pubkeys 9multi sig) // limit: use of OP_RETURN makes embedding data in tx difficult // OP_RETURN limit is 40 bytes Console.ReadLine(); }