Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
 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);
 }
Пример #4
0
        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);
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
        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);
            }
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
            }
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
        }
Пример #12
0
		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);
		}
Пример #13
0
		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;
		}
Пример #14
0
		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);
		}