private static PSBT BuildPsbt(Network network, HDFingerprint fingerprint, ExtPubKey xpub, KeyPath xpubKeyPath) { var deriveSendFromKeyPath = new KeyPath("1/0"); var deriveSendToKeyPath = new KeyPath("0/0"); KeyPath sendFromKeyPath = xpubKeyPath.Derive(deriveSendFromKeyPath); KeyPath sendToKeyPath = xpubKeyPath.Derive(deriveSendToKeyPath); PubKey sendFromPubKey = xpub.Derive(deriveSendFromKeyPath).PubKey; PubKey sendToPubKey = xpub.Derive(deriveSendToKeyPath).PubKey; BitcoinAddress sendFromAddress = sendFromPubKey.GetAddress(ScriptPubKeyType.Segwit, network); BitcoinAddress sendToAddress = sendToPubKey.GetAddress(ScriptPubKeyType.Segwit, network); TransactionBuilder builder = network.CreateTransactionBuilder(); builder = builder.AddCoins(new Coin(uint256.One, 0, Money.Coins(1), sendFromAddress.ScriptPubKey)); builder.Send(sendToAddress.ScriptPubKey, Money.Coins(0.99999m)); PSBT psbt = builder .SendFees(Money.Coins(0.00001m)) .BuildPSBT(false); var rootKeyPath1 = new RootedKeyPath(fingerprint, sendFromKeyPath); var rootKeyPath2 = new RootedKeyPath(fingerprint, sendToKeyPath); psbt.AddKeyPath(sendFromPubKey, rootKeyPath1, sendFromAddress.ScriptPubKey); psbt.AddKeyPath(sendToPubKey, rootKeyPath2, sendToAddress.ScriptPubKey); return(psbt); }
public void CanUseKeyPath() { var keyPath = KeyPath.Parse("0/1/2/3"); Assert.Equal(keyPath.ToString(), "0/1/2/3"); var key = new ExtKey(); Assert.Equal(key .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), key.Derive(keyPath).ToString(Network.Main)); var neuter = key.Neuter(); Assert.Equal(neuter .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), neuter.Derive(keyPath).ToString(Network.Main)); Assert.Equal(neuter.Derive(keyPath).ToString(Network.Main), key.Derive(keyPath).Neuter().ToString(Network.Main)); keyPath = new KeyPath(0x8000002Cu, 1u); Assert.Equal(keyPath.ToString(), "44'/1"); keyPath = KeyPath.Parse("44'/1"); Assert.False(keyPath.IsHardened); Assert.True(KeyPath.Parse("44'/1'").IsHardened); Assert.Equal(keyPath[0], 0x8000002Cu); Assert.Equal(keyPath[1], 1u); key = new ExtKey(); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); keyPath = KeyPath.Parse(""); keyPath = keyPath.Derive(44, true).Derive(1, false); Assert.Equal(keyPath.ToString(), "44'/1"); Assert.Equal(keyPath.Increment().ToString(), "44'/2"); Assert.Equal(keyPath.Derive(1, true).Increment().ToString(), "44'/1/2'"); Assert.Equal(keyPath.Parent.ToString(), "44'"); Assert.Equal(keyPath.Parent.Parent.ToString(), ""); Assert.Equal(keyPath.Parent.Parent.Parent, null); Assert.Equal(keyPath.Parent.Parent.Increment(), null); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); Assert.True(key.Derive(44, true).IsHardened); Assert.False(key.Derive(44, false).IsHardened); neuter = key.Derive(44, true).Neuter(); Assert.True(neuter.IsHardened); neuter = key.Derive(44, false).Neuter(); Assert.False(neuter.IsHardened); }
internal HDKeyManager(ExtKey extKey, KeyPath accountKeyPath, uint minGapLimit = AbsoluteMinGapLimit) { AccountKeyPath = accountKeyPath; ExtKey = extKey; ExtPubKey = extKey.Derive(AccountKeyPath).Neuter(); MinGapLimit = minGapLimit; ExternalKeys = new KeysRegistry(ExtPubKey.Derive(0), AccountKeyPath.Derive(0), MinGapLimit); InternalKeys = new KeysRegistry(ExtPubKey.Derive(1), AccountKeyPath.Derive(1), MinGapLimit); }
public void CanUseKeyPath() { var keyPath = KeyPath.Parse("0/1/2/3"); Assert.Equal("0/1/2/3", keyPath.ToString()); var key = new ExtKey(); Assert.Equal(key .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.PurpleMain), key.Derive(keyPath).ToString(Network.PurpleMain)); var neuter = key.Neuter(); Assert.Equal(neuter .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.PurpleMain), neuter.Derive(keyPath).ToString(Network.PurpleMain)); Assert.Equal(neuter.Derive(keyPath).ToString(Network.PurpleMain), key.Derive(keyPath).Neuter().ToString(Network.PurpleMain)); keyPath = new KeyPath(new uint[] { 0x8000002Cu, 1u }); Assert.Equal("44'/1", keyPath.ToString()); keyPath = KeyPath.Parse("44'/1"); Assert.False(keyPath.IsHardened); Assert.True(KeyPath.Parse("44'/1'").IsHardened); Assert.Equal(0x8000002Cu, keyPath[0]); Assert.Equal(1u, keyPath[1]); key = new ExtKey(); Assert.Equal(key.Derive(keyPath).ToString(Network.PurpleMain), key.Derive(44, true).Derive(1, false).ToString(Network.PurpleMain)); keyPath = KeyPath.Parse(""); keyPath = keyPath.Derive(44, true).Derive(1, false); Assert.Equal("44'/1", keyPath.ToString()); Assert.Equal("44'/2", keyPath.Increment().ToString()); Assert.Equal("44'/1/2'", keyPath.Derive(1, true).Increment().ToString()); Assert.Equal("44'", keyPath.Parent.ToString()); Assert.Equal("", keyPath.Parent.Parent.ToString()); Assert.Null(keyPath.Parent.Parent.Parent); Assert.Null(keyPath.Parent.Parent.Increment()); Assert.Equal(key.Derive(keyPath).ToString(Network.PurpleMain), key.Derive(44, true).Derive(1, false).ToString(Network.PurpleMain)); Assert.True(key.Derive(44, true).IsHardened); Assert.False(key.Derive(44, false).IsHardened); neuter = key.Derive(44, true).Neuter(); Assert.True(neuter.IsHardened); neuter = key.Derive(44, false).Neuter(); Assert.False(neuter.IsHardened); }
/// <summary> /// Create new non-root address /// </summary> public void CreateCompanyAddress(long addressCategory, string name, string account) { long addressType = (long)CustomAddressType.Company; var rootXPrivKey = ExtKey.Parse(CurrentWallet.RootXPrivKey); var parentKeyPath = new KeyPath($"/{addressType}'/{addressCategory}'"); long maxIndex = AddressDao.GetMaxPathIndex(CurrentWallet.Id, parentKeyPath.ToString()); long currentIndex = maxIndex + 1; var keyPath = parentKeyPath.Derive((uint)currentIndex); var address = KeyOperator.Instance.DeriveNewAddress(rootXPrivKey, keyPath); var addressInfo = new AddressInfo { Id = Guid.NewGuid().ToString("N"), Address = address, ExtPubKeyWif = null, WalletId = CurrentWallet.Id, Network = (NetworkOperator.Instance.Network == Network.Main) ? (int)NetworkType.Mainnet : (int)NetworkType.Testnet, KeyPath = keyPath.ToString(), ParentKeyPath = parentKeyPath.ToString(), PathIndex = currentIndex, AddressType = addressType, AddressCategory = addressCategory, Name = (addressCategory == (long)AddressCategory.Receiver ? "收款" : "付款") + " " + currentIndex, Account = account }; AddressDao.Create(addressInfo); }
public HdPubKey GenerateNewKey(string label, KeyState keyState, bool isInternal, bool toFile = true) { // BIP44-ish derivation scheme // m / purpose' / coin_type' / account' / change / address_index var change = isInternal ? 1 : 0; lock (HdPubKeysLock) { IEnumerable <HdPubKey> relevantHdPubKeys; if (isInternal) { relevantHdPubKeys = HdPubKeys.Where(x => x.IsInternal()); } else { relevantHdPubKeys = HdPubKeys.Where(x => !x.IsInternal()); } KeyPath path; if (!relevantHdPubKeys.Any()) { path = new KeyPath($"{change}/0"); } else { int largestIndex = relevantHdPubKeys.Max(x => x.GetIndex()); List <int> missingIndexes = Enumerable.Range(0, largestIndex).Except(relevantHdPubKeys.Select(x => x.GetIndex())).ToList(); if (missingIndexes.Any()) { int smallestMissingIndex = missingIndexes.Min(); path = relevantHdPubKeys.First(x => x.GetIndex() == (smallestMissingIndex - 1)).GetNonHardenedKeyPath().Increment(); } else { path = relevantHdPubKeys.First(x => x.GetIndex() == largestIndex).GetNonHardenedKeyPath().Increment(); } } var fullPath = AccountKeyPath.Derive(path); var pubKey = ExtPubKey.Derive(path).PubKey; var hdPubKey = new HdPubKey(pubKey, fullPath, label, keyState); HdPubKeys.Add(hdPubKey); if (toFile) { ToFile(); } return(hdPubKey); } }
private void LoadPool(KeyPath keyPath) { var lastLoaded = GetLastLoaded(keyPath); var isInternal = IsInternal(keyPath); var tracker = Tracker; for (int i = lastLoaded; i < lastLoaded + _KeyPoolSize; i++) { var childPath = keyPath.Derive(i, false); AddKnown(childPath, tracker, isInternal); } IncrementLastLoaded(keyPath, _KeyPoolSize); }
public Script GetNewDestination() { while (true) { var index = Repository.Get <uint>(_WalletId, ""); var address = _ExtPubKey.Derive((uint)index).PubKey.Hash.ScriptPubKey; index++; bool conflict = false; Repository.UpdateOrInsert(_WalletId, "", index, (o, n) => { conflict = o + 1 != n; return(n); }); if (conflict) { continue; } Repository.UpdateOrInsert <uint?>(_WalletId, address.Hash.ToString(), (uint)(index - 1), (o, n) => n); var path = _DerivationPath.Derive((uint)(index - 1)); Logs.Wallet.LogInformation($"Created address {address.GetDestinationAddress(_Network)} of with HD path {path}"); return(address); } }
public Script GetNextScriptPubKey(KeyPath keyPath) { AssertGroupAffected(); Script result; lock (cs) { var currentIndex = GetNextIndex(keyPath); KeyPath childPath = keyPath.Derive(currentIndex, false); result = GetScriptPubKey(childPath, false); IncrementCurrentIndex(keyPath); if (_KeyPoolSize != 0) { var created = (double)(currentIndex + 1) / (double)GetLastLoaded(keyPath); if (created > 0.9) { LoadPool(keyPath); RefreshFilter(); } } else { AddKnown(childPath); if (_Group != null) { foreach (var node in _Group.ConnectedNodes) { #pragma warning disable CS0612 // Type or member is obsolete var tracker = node.Behaviors.Find <TrackerBehavior>(); #pragma warning restore CS0612 // Type or member is obsolete if (tracker == null) { continue; } foreach (var data in result.ToOps().Select(o => o.PushData).Where(o => o != null)) { tracker.SendMessageAsync(new FilterAddPayload(data)); } } } } } return(result); }
public HdPubKey GenerateNewKey(string label, KeyState keyState, bool isInternal, bool toFile = true) { // BIP44-ish derivation scheme // m / purpose' / coin_type' / account' / change / address_index lock (HdPubKeysLock) { var change = isInternal ? 1 : 0; IEnumerable <HdPubKey> relevantHdPubKeys; if (isInternal) { relevantHdPubKeys = HdPubKeys.Where(x => x.IsInternal()); } else { relevantHdPubKeys = HdPubKeys.Where(x => !x.IsInternal()); } KeyPath path; if (!relevantHdPubKeys.Any()) { path = new KeyPath($"{change}/0"); } else { path = relevantHdPubKeys.OrderBy(x => x.GetIndex()).Last().GetNonHardenedKeyPath().Increment(); } var fullPath = AccountKeyPath.Derive(path); var pubKey = ExtPubKey.Derive(path).PubKey; var hdPubKey = new HdPubKey(pubKey, fullPath, label, keyState); HdPubKeys.Add(hdPubKey); if (toFile) { ToFile(); } return(hdPubKey); } }
public void CanUseKeyPath() { var keyPath = KeyPath.Parse("0/1/2/3"); Assert.Equal("0/1/2/3", keyPath.ToString()); var key = new ExtKey(); Assert.Equal(key .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), key.Derive(keyPath).ToString(Network.Main)); var neuter = key.Neuter(); Assert.Equal(neuter .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), neuter.Derive(keyPath).ToString(Network.Main)); Assert.Equal(neuter.Derive(keyPath).ToString(Network.Main), key.Derive(keyPath).Neuter().ToString(Network.Main)); keyPath = new KeyPath(new uint[] { 0x8000002Cu, 1u }); Assert.Equal("44'/1", keyPath.ToString()); keyPath = KeyPath.Parse("44'/1"); Assert.False(keyPath.IsHardened); Assert.True(KeyPath.Parse("44'/1'").IsHardened); Assert.Equal(0x8000002Cu, keyPath[0]); Assert.Equal(1u, keyPath[1]); key = new ExtKey(); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); keyPath = KeyPath.Parse(""); keyPath = keyPath.Derive(44, true).Derive(1, false); Assert.Equal("44'/1", keyPath.ToString()); Assert.Equal("44'/2", keyPath.Increment().ToString()); Assert.Equal("44'/1/2'", keyPath.Derive(1, true).Increment().ToString()); Assert.Equal("44'", keyPath.Parent.ToString()); Assert.Equal("", keyPath.Parent.Parent.ToString()); Assert.Null(keyPath.Parent.Parent.Parent); Assert.Throws <InvalidOperationException>(() => keyPath.Parent.Parent.Increment()); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); Assert.True(key.Derive(44, true).IsHardened); Assert.False(key.Derive(44, false).IsHardened); neuter = key.Derive(44, true).Neuter(); Assert.True(neuter.IsHardened); neuter = key.Derive(44, false).Neuter(); Assert.False(neuter.IsHardened); KeyPath a = new KeyPath("3/2"); KeyPath b = new KeyPath("5/6"); Assert.Equal(new KeyPath("3/2/5/6"), a + b); b = null; Assert.Equal(a, a + b); a = null; Assert.Null(a + b); b = new KeyPath("5/6"); Assert.Equal(new KeyPath("5/6"), a + b); a += new KeyPath("3/2"); a += new KeyPath("5/6"); Assert.Equal(new KeyPath("3/2/5/6"), a); }
public void CanUseKeyPath() { var keyPath = KeyPath.Parse("0/1/2/3"); Assert.Equal(keyPath.ToString(), "0/1/2/3"); var key = new ExtKey(); Assert.Equal(key .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), key.Derive(keyPath).ToString(Network.Main)); var neuter = key.Neuter(); Assert.Equal(neuter .Derive(0) .Derive(1) .Derive(2) .Derive(3) .ToString(Network.Main), neuter.Derive(keyPath).ToString(Network.Main)); Assert.Equal(neuter.Derive(keyPath).ToString(Network.Main), key.Derive(keyPath).Neuter().ToString(Network.Main)); keyPath = new KeyPath(new uint[] { 0x8000002Cu, 1u }); Assert.Equal(keyPath.ToString(), "44'/1"); keyPath = KeyPath.Parse("44'/1"); Assert.False(keyPath.IsHardened); Assert.True(KeyPath.Parse("44'/1'").IsHardened); Assert.Equal(keyPath[0], 0x8000002Cu); Assert.Equal(keyPath[1], 1u); key = new ExtKey(); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); keyPath = KeyPath.Parse(""); keyPath = keyPath.Derive(44, true).Derive(1, false); Assert.Equal(keyPath.ToString(), "44'/1"); Assert.Equal(keyPath.Increment().ToString(), "44'/2"); Assert.Equal(keyPath.Derive(1,true).Increment().ToString(), "44'/1/2'"); Assert.Equal(keyPath.Parent.ToString(), "44'"); Assert.Equal(keyPath.Parent.Parent.ToString(), ""); Assert.Equal(keyPath.Parent.Parent.Parent, null); Assert.Equal(keyPath.Parent.Parent.Increment(), null); Assert.Equal(key.Derive(keyPath).ToString(Network.Main), key.Derive(44, true).Derive(1, false).ToString(Network.Main)); Assert.True(key.Derive(44, true).IsHardened); Assert.False(key.Derive(44, false).IsHardened); neuter = key.Derive(44, true).Neuter(); Assert.True(neuter.IsHardened); neuter = key.Derive(44, false).Neuter(); Assert.False(neuter.IsHardened); }
public Script GetNextScriptPubKey(KeyPath keyPath) { AssertGroupAffected(); Script result; lock(cs) { var currentIndex = GetNextIndex(keyPath); KeyPath childPath = keyPath.Derive(currentIndex, false); result = GetScriptPubKey(childPath, false); IncrementCurrentIndex(keyPath); if(_KeyPoolSize != 0) { var created = (double)(currentIndex + 1) / (double)GetLastLoaded(keyPath); if(created > 0.9) { LoadPool(keyPath); RefreshFilter(); } } else { AddKnown(childPath); if(_Group != null) { foreach(var node in _Group.ConnectedNodes) { var tracker = node.Behaviors.Find<TrackerBehavior>(); if(tracker == null) continue; foreach(var data in result.ToOps().Select(o => o.PushData).Where(o => o != null)) { tracker.SendMessageAsync(new FilterAddPayload(data)); } } } } } return result; }
private void LoadPool(KeyPath keyPath) { var lastLoaded = GetLastLoaded(keyPath); var isInternal = IsInternal(keyPath); var tracker = Tracker; for(int i = lastLoaded ; i < lastLoaded + _KeyPoolSize ; i++) { var childPath = keyPath.Derive(i, false); AddKnown(childPath, tracker, isInternal); } IncrementLastLoaded(keyPath, _KeyPoolSize); }