Ejemplo n.º 1
0
 private void RunTest(TestVector test)
 {
     var seed = TestUtils.ParseHex(test.strHexMaster);
     ExtKey key = new ExtKey(seed);
     ExtPubKey pubkey = key.Neuter();
     foreach(TestDerivation derive in test.vDerive)
     {
         byte[] data = key.ToBytes();
         Assert.Equal(74, data.Length);
         data = pubkey.ToBytes();
         Assert.Equal(74, data.Length);
         // Test private key
         BitcoinExtKey b58key = Network.Main.CreateBitcoinExtKey(key);
         Assert.True(b58key.ToString() == derive.prv);
         // Test public key
         BitcoinExtPubKey b58pubkey = Network.Main.CreateBitcoinExtPubKey(pubkey);
         Assert.True(b58pubkey.ToString() == derive.pub);
         // Derive new keys
         ExtKey keyNew = key.Derive(derive.nChild);
         ExtPubKey pubkeyNew = keyNew.Neuter();
         if(!((derive.nChild & 0x80000000) != 0))
         {
             // Compare with public derivation
             ExtPubKey pubkeyNew2 = pubkey.Derive(derive.nChild);
             Assert.True(pubkeyNew == pubkeyNew2);
         }
         key = keyNew;
         pubkey = pubkeyNew;
     }
 }
Ejemplo n.º 2
0
        public static WalletModel HashTable2Wallet(Hashtable hashT)
        {
            var walletFile          = hashT["File"].ToString();
            var walletFileRecover   = hashT["FileRecover"].ToString();
            var walletId            = hashT["Id"].ToString();
            var walletName          = hashT["Name"].ToString();
            var walletPassHash      = hashT["PassHash"].ToString();
            var walletRecPhraseHash = hashT["RecPhraseHash"].ToString();
            var walletDescription   = hashT["Description"].ToString();
            var walletNetwork       = Network.GetNetwork(hashT["Network"].ToString());
            var walletMasterKey     = ExtKey.Parse(hashT["MasterKey"].ToString(), walletNetwork);
            var walletCreated       = (DateTimeOffset)hashT["Created"];

            BitcoinExtPubKey rootKey = Generators.GenerateRootKey(walletMasterKey, walletNetwork);
            var wallet = new WalletModel(id: walletId, newName: walletName, rootKey: rootKey, newCreated: walletCreated, description: walletDescription);

            wallet.FileRecover        = walletFileRecover;
            wallet.PassHash           = walletPassHash;
            wallet.RecoveryPhraseHash = walletRecPhraseHash;
            wallet.SetMasterKey(walletMasterKey);
            return(wallet);
        }
Ejemplo n.º 3
0
        public static void HdWallet1()
        {
            ExtKey d = new ExtKey();


            string Path      = "m/44'/60'/0'/0/x";
            string words     = "";
            var    mneumonic = new Mnemonic(words);
            var    seed      = mneumonic.DeriveSeed("123456@a").ToHex();
            var    masterKey = new ExtKey(seed);
            var    d1        = new Account(masterKey.PrivateKey.ToBytes()).Address;

            for (int i = 0; i < 10; i++)
            {
                var    keyPath    = new NBitcoin.KeyPath(Path.Replace("x", i.ToString()));
                ExtKey extKey     = masterKey.Derive(keyPath);
                byte[] privateKey = extKey.PrivateKey.ToBytes();
                var    account    = new Account(privateKey);

                Console.WriteLine("Account index : " + i + " - Address : " + account.Address + " - Private key : " + account.PrivateKey);
            }
        }
Ejemplo n.º 4
0
        public static Safe Load(string password, string walletFilePath)
        {
            if (!File.Exists(walletFilePath))
            {
                throw new Exception("WalletFileDoesNotExists");
            }

            var walletFileRawContent = WalletFileSerializer.Deserialize(walletFilePath);

            var encryptedBitcoinPrivateKeyString = walletFileRawContent.EncryptedSeed;
            var chainCodeString = walletFileRawContent.ChainCode;

            var chainCode = Convert.FromBase64String(chainCodeString);

            Network network;
            var     networkString = walletFileRawContent.Network;

            if (networkString == Network.MainNet.ToString())
            {
                network = Network.MainNet;
            }
            else if (networkString == Network.TestNet.ToString())
            {
                network = Network.TestNet;
            }
            else
            {
                throw new Exception("NotRecognizedNetworkInWalletFile");
            }

            var safe = new Safe(password, walletFilePath, network);

            var privateKey = Key.Parse(encryptedBitcoinPrivateKeyString, password, safe._network);
            var seedExtKey = new ExtKey(privateKey, chainCode);

            safe.SetSeed(seedExtKey);

            return(safe);
        }
Ejemplo n.º 5
0
        public static (Bitcoin.Features.Wallet.Wallet wallet, ExtKey key) GenerateBlankWalletWithExtKey(string name, string password)
        {
            Mnemonic mnemonic    = new Mnemonic(Wordlist.English, WordCount.Twelve);
            ExtKey   extendedKey = mnemonic.DeriveExtKey(password);

            Bitcoin.Features.Wallet.Wallet walletFile = new Bitcoin.Features.Wallet.Wallet
            {
                Name          = name,
                EncryptedSeed = extendedKey.PrivateKey.GetEncryptedBitcoinSecret(password, Network.Main).ToWif(),
                ChainCode     = extendedKey.ChainCode,
                CreationTime  = DateTimeOffset.Now,
                Network       = Network.Main,
                AccountsRoot  = new List <AccountRoot> {
                    new AccountRoot()
                    {
                        Accounts = new List <HdAccount>(), CoinType = (CoinType)Network.Main.Consensus.CoinType
                    }
                },
            };

            return(walletFile, extendedKey);
        }
        /// <inheritdoc />
        public Mnemonic CreateWallet(string password, string name, string passphrase = null, string mnemonicList = null)
        {
            Guard.NotEmpty(password, nameof(password));
            Guard.NotEmpty(name, nameof(name));

            // for now the passphrase is set to be the password by default.
            if (passphrase == null)
            {
                passphrase = password;
            }

            // generate the root seed used to generate keys from a mnemonic picked at random
            // and a passphrase optionally provided by the user
            Mnemonic mnemonic = string.IsNullOrEmpty(mnemonicList)
                ? new Mnemonic(Wordlist.English, WordCount.Twelve)
                : new Mnemonic(mnemonicList);
            ExtKey extendedKey = HdOperations.GetHdPrivateKey(mnemonic, passphrase);

            // create a wallet file
            Wallet wallet = this.GenerateWalletFile(password, name, extendedKey);

            // generate multiple accounts and addresses from the get-go
            for (int i = 0; i < WalletCreationAccountsCount; i++)
            {
                HdAccount account = wallet.AddNewAccount(password, this.coinType);
                account.CreateAddresses(this.network, UnusedAddressesBuffer);
                account.CreateAddresses(this.network, UnusedAddressesBuffer, true);
            }

            // update the height of the we start syncing from
            this.UpdateLastBlockSyncedHeight(wallet, this.chain.Tip);

            // save the changes to the file and add addresses to be tracked
            this.SaveToFile(wallet);
            this.Load(wallet);
            this.LoadKeysLookup();

            return(mnemonic);
        }
Ejemplo n.º 7
0
    public void SubmitSeed()
    {
        if (submitSeedText.text != null && submitPWText.text.Length > 7)
        {
            try {
                string   seedWord = submitSeedText.text;
                string   password = submitPWText.text;
                Mnemonic mnemo    = new Mnemonic(seedWord, Wordlist.English);
                ExtKey   hdRoot   = mnemo.DeriveExtKey(password);



                CancleForgotPanel();

                Debug.Log("New password:" + password);
            }
            catch (FormatException)
            {
                missingText.enabled = true;
            }
        }
    }
        public static (Wallet wallet, ExtKey key) GenerateBlankWalletWithExtKey(string name, string password)
        {
            Mnemonic mnemonic    = new Mnemonic("grass industry beef stereo soap employ million leader frequent salmon crumble banana");
            ExtKey   extendedKey = mnemonic.DeriveExtKey(password);

            Wallet walletFile = new Wallet
            {
                Name          = name,
                EncryptedSeed = extendedKey.PrivateKey.GetEncryptedBitcoinSecret(password, Network.Main).ToWif(),
                ChainCode     = extendedKey.ChainCode,
                CreationTime  = DateTimeOffset.Now,
                Network       = Network.Main,
                AccountsRoot  = new List <AccountRoot> {
                    new AccountRoot()
                    {
                        Accounts = new List <HdAccount>(), CoinType = (CoinType)Network.Main.Consensus.CoinType
                    }
                },
            };

            return(walletFile, extendedKey);
        }
Ejemplo n.º 9
0
        static void CorrectOne(string basePath, Network net, string words, ScriptPubKeyType scriptPubKeyType, string password = null)
        {
            Console.WriteLine("-------------------------------------");
            Console.WriteLine($" basePath: {basePath}");
            Console.WriteLine($" words: {words}");
            Console.WriteLine();
            //https://stackoverflow.com/questions/46550818/nbitcoin-and-mnemonic-standards
            Mnemonic mnemo  = new Mnemonic(words, Wordlist.English);
            ExtKey   hdroot = mnemo.DeriveExtKey(password);

            for (int i = 0; i < 10; i++)
            {
                var privkey    = hdroot.Derive(new NBitcoin.KeyPath(basePath + "/0/" + i.ToString()));
                var publicKey  = privkey.Neuter().PubKey;
                var privateKey = privkey.Neuter().GetWif(net);
                var address    = publicKey.GetAddress(scriptPubKeyType, net).ToString();
                Console.WriteLine($"{ net.ToString() } , public key : { address} , privateKey {privateKey}");
                Console.WriteLine("");
            }


            ExtKey masterKey        = hdroot.Derive(new NBitcoin.KeyPath(basePath));
            string masterPublicKey  = masterKey.Neuter().GetWif(net).ToString();
            string masterPrivateKey = masterKey.GetWif(net).ToString();

            Console.WriteLine($"");
            Console.WriteLine($"MasterPrivateKey : {net.ToString()} {masterPrivateKey} ");
            Console.WriteLine($"MasterPublicKey  : {net.ToString()} {masterPublicKey} ");
            Console.WriteLine($"");
            Console.WriteLine("-------------------------------------");


            /* Console.WriteLine("Master key 11111 : " + hdroot.ToString(net));
             * ExtPubKey masterPubKey = hdroot.Neuter();
             * Console.WriteLine("Master PubKey  " + masterPubKey.ToString(net));
             * Console.WriteLine();
             * Console.WriteLine();
             */
        }
Ejemplo n.º 10
0
        /// <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);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Interactive example that walks through creating, signing, and broadcasting a Dash transaction.
        /// Make sure you have Dash Core wallet running on TestNet for this example to work.
        /// </summary>
        public static void RunExample()
        {
            // Create a new HD extended private key
            ExtKey extKey = CreateExtKey();

            // Derive a child address so we can send it some coins
            var    extPubKey                  = extKey.Neuter().GetWif(_network);
            int    customerId                 = 1234567;
            string keyDerivationPath          = $"{customerId}/0";
            BitcoinPubKeyAddress childAddress = DeriveChildAddress(extPubKey.ToString(), keyDerivationPath);

            // Watch this address in your core wallet so that you can monitor it for funds
            RPCClient rpc = GetRpcClient();

            rpc.ImportAddress(childAddress, $"Customer {customerId}", false);

            // Send some coins manually to the address just created
            Console.WriteLine($"Now, go to another wallet and send some coins to address {childAddress}.");
            Console.WriteLine($"Wait a few minutes for it to confirm, then press ENTER to continue.");
            Console.ReadLine();

            // Create and sign a transaction that spends the coins received by the address above.
            Console.WriteLine($"Enter an address of yours to which you want to receive coins:");
            string toAddr = Console.ReadLine();

            Console.WriteLine($"Enter the amount to spend:");
            string spend = Console.ReadLine();

            decimal minerFee      = 0.00001m;
            decimal amountToSpend = Convert.ToDecimal(spend) - minerFee;

            Key privateKey = extKey.Derive(KeyPath.Parse(keyDerivationPath)).PrivateKey;

            var transaction = CreateAndSignTransaction(childAddress.ToString(), toAddr, amountToSpend, privateKey);

            // Finally, broadcast transaction to network
            rpc.SendRawTransaction(transaction);
        }
        /// <summary>
        /// Loads all the private keys for each of the <see cref="HdAddress"/> in <see cref="TransactionBuildContext.UnspentOutputs"/>
        /// </summary>
        /// <param name="context">The context associated with the current transaction being built.</param>
        /// <param name="coinsSpent">The coins spent to generate the transaction.</param>
        protected void AddSecrets(TransactionBuildContext context, IEnumerable <ICoin> coinsSpent)
        {
            if (!context.Sign)
            {
                return;
            }

            Wallet wallet     = this.walletManager.GetWallet(context.AccountReference.WalletName);
            ExtKey seedExtKey = this.walletManager.GetExtKey(context.AccountReference, context.WalletPassword);

            var signingKeys = new HashSet <ISecret>();
            Dictionary <OutPoint, UnspentOutputReference> outpointLookup = context.UnspentOutputs.ToDictionary(o => o.ToOutPoint(), o => o);
            IEnumerable <string> uniqueHdPaths = coinsSpent.Select(s => s.Outpoint).Select(o => outpointLookup[o].Address.HdPath).Distinct();

            foreach (string hdPath in uniqueHdPaths)
            {
                ExtKey        addressExtKey     = seedExtKey.Derive(new KeyPath(hdPath));
                BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network);
                signingKeys.Add(addressPrivateKey);
            }

            context.TransactionBuilder.AddKeys(signingKeys.ToArray());
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Unlock a locked wallet.
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool Unlock(string password)
        {
            if (IsUnlocked || !IsCreated)
            {
                return(IsUnlocked && IsCreated);
            }

            try
            {
                var masterKey = Key.Parse(_walletFile.WifKey, password, _network);
                _extKey = new ExtKey(masterKey, _walletFile.ChainCode);
            }
            catch (SecurityException ex)
            {
                Log.Error(ex);
                return(false);
            }

            // finally load all keys
            LoadKeys();

            return(true);
        }
Ejemplo n.º 14
0
        internal BitcoinExtKey GetPrivateKey(int index, HdPathType hdPathType = HdPathType.Receive)
        {
            KeyPath keyPath;

            if (hdPathType == HdPathType.Receive)
            {
                keyPath = new KeyPath($"{ReceiveHdPath}/{index}'");
            }
            else if (hdPathType == HdPathType.Change)
            {
                keyPath = new KeyPath($"{ChangeHdPath}/{index}'");
            }
            else if (hdPathType == HdPathType.NonHardened)
            {
                keyPath = new KeyPath($"{NonHardenedHdPath}/{index}");
            }
            else
            {
                throw new Exception("HdPathType not exists");
            }

            return(ExtKey.Derive(keyPath).GetWif(Network));
        }
        public void CanGenerateAddress()
        {
            using (var server = TumblerServerTester.Create())
            {
                var key = new ExtKey().GetWif(Network.RegTest);
                var w   = new ClientDestinationWallet(key.Neuter(), new KeyPath("0/1"), server.ClientRuntime.Repository, server.ClientRuntime.Network);

                var k1 = w.GetNewDestination();
                var k2 = w.GetNewDestination();
                Assert.Equal(new KeyPath("0/1/0"), w.GetKeyPath(k1));
                Assert.Equal(new KeyPath("0/1/1"), w.GetKeyPath(k2));
                Assert.Null(w.GetKeyPath(new Key().ScriptPubKey));

                var wrpc = new RPCDestinationWallet(server.AliceNode.CreateRPCClient());

                k1 = wrpc.GetNewDestination();
                k2 = wrpc.GetNewDestination();

                Assert.Equal(new KeyPath("0'/0'/1'"), wrpc.GetKeyPath(k1));
                Assert.Equal(new KeyPath("0'/0'/2'"), wrpc.GetKeyPath(k2));
                Assert.Null(w.GetKeyPath(new Key().ScriptPubKey));
            }
        }
Ejemplo n.º 16
0
        public IActionResult About(string transactonHex = "dc7268d85689fe3a4dade2a5886794237221fb0161e59f12d8128c46ca7fab90")
        {
            ViewData["Message"] = "Your application description page.";

            var network = Network.TestNet;

            //mysLDbXCoie81EysydNHfSTva5sy4rRzKB

            var secret = new ExtKey().GetWif(network); //new BitcoinSecret("943b00f1-1488-4d44-91fa-f3bcc5789099");
            var key    = secret.PrivateKey;
            var client = new QBitNinjaClient(network);

            Transaction tx    = new Transaction();
            var         input = new TxIn();

            var transactionId = uint256.Parse("dc7268d85689fe3a4dade2a5886794237221fb0161e59f12d8128c46ca7fab90");

            //  var transactionResponse = client.GetTransaction(transactionId).Result;

            input.PrevOut   = new OutPoint(new uint256(transactonHex), 1); //Transaction ID
            input.ScriptSig = key.GetBitcoinSecret(network).GetAddress().ScriptPubKey;
            tx.AddInput(input);

            TxOut output       = new TxOut();
            var   desctination = BitcoinAddress.Create("mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf");
            Money fee          = Money.Satoshis(40000);

            output.Value        = Money.Coins(0.1m) - fee;
            output.ScriptPubKey = desctination.ScriptPubKey;
            tx.AddOutput(output);

            tx.Sign(secret, false);

            BroadcastResponse broadcastResponse = client.Broadcast(tx).Result;

            return(View());
        }
Ejemplo n.º 17
0
            bool AddTransaction(ExtKey key, TxId txId)
            {
                bool outputAdded = false;

                var txResponse = Spend.Api.GetTransaction(txId.TransactionId).Result;

                if (txResponse.Block == null)
                {
                    return(false);
                }

                var      receivedCoins   = txResponse.ReceivedCoins;
                OutPoint outPointToSpend = null;

                foreach (var coin in receivedCoins)
                {
                    if (coin.TxOut.ScriptPubKey == key.PrivateKey.ScriptPubKey)
                    {
                        outPointToSpend = coin.Outpoint;
                        txId.MoneyOut  += (Money)coin.Amount;
                        coins.Add(coin);
                    }
                }

                if (outPointToSpend != null)
                {
                    Tx.Inputs.Add(
                        new TxIn()
                    {
                        PrevOut   = outPointToSpend,
                        ScriptSig = key.PrivateKey.GetWif(Spend.Api.Network).GetAddress(ScriptPubKeyType.Legacy).ScriptPubKey
                    });
                    outputAdded = true;
                }

                return(outputAdded);
            }
Ejemplo n.º 18
0
        /// <inheritdoc />
        public Wallet RecoverWallet(string password, string name, string mnemonic, DateTime creationTime, string passphrase = null)
        {
            Guard.NotEmpty(password, nameof(password));
            Guard.NotEmpty(name, nameof(name));
            Guard.NotEmpty(mnemonic, nameof(mnemonic));

            // for now the passphrase is set to be the password by default.
            if (passphrase == null)
            {
                passphrase = password;
            }

            // generate the root seed used to generate keys
            ExtKey extendedKey = HdOperations.GetHdPrivateKey(mnemonic, passphrase);

            // create a wallet file
            Wallet wallet = this.GenerateWalletFile(password, name, extendedKey, creationTime);

            // generate multiple accounts and addresses from the get-go
            for (int i = 0; i < WalletRecoveryAccountsCount; i++)
            {
                HdAccount account = wallet.AddNewAccount(password, this.coinType);
                account.CreateAddresses(this.network, UnusedAddressesBuffer);
                account.CreateAddresses(this.network, UnusedAddressesBuffer, true);
            }

            int blockSyncStart = this.chain.GetHeightAtTime(creationTime);

            this.UpdateLastBlockSyncedHeight(wallet, this.chain.GetBlock(blockSyncStart));

            // save the changes to the file and add addresses to be tracked
            this.SaveToFile(wallet);
            this.Load(wallet);
            this.LoadKeysLookup();

            return(wallet);
        }
Ejemplo n.º 19
0
        private static void MasterPublicKeyAndPrivateKey(string path, Network net, string words, string password)
        {
            //  Mnemonic mnemo = new Mnemonic(Wordlist.English, WordCount.Twelve);
            Mnemonic mnemo  = new Mnemonic(words);
            ExtKey   extKey = mnemo.DeriveExtKey(password);

            ExtKey masterKey        = extKey.Derive(new NBitcoin.KeyPath(path));
            string masterPublicKey  = masterKey.Neuter().GetWif(net).ToString();
            string masterPrivateKey = masterKey.GetWif(net).ToString();

            Console.WriteLine($"");
            Console.WriteLine($"MasterPrivateKey : {net.ToString()} {masterPrivateKey} ");
            Console.WriteLine($"MasterPublicKey  : {net.ToString()} {masterPublicKey} ");
            Console.WriteLine($"");

            var    drivekey   = masterKey.Derive(0);
            string publicKey  = masterKey.Neuter().GetWif(net).ToString();
            string privateKey = masterKey.GetWif(net).ToString();

            Console.WriteLine($"");
            Console.WriteLine($"first publicKey : {net.ToString()} {publicKey} ");
            Console.WriteLine($"first privateKey  : {net.ToString()} {privateKey} ");
            Console.WriteLine($"");
        }
        private void Refresh()
        {
            try
            {
                ExtKey changeKey = tcBitcoin.GetExtendedKey(change.FullHDPath);

                textKeyNamespace.Text  = change.KeyNamespace;
                textKeyPath.Text       = change.FullHDPath;
                textChangeStatus.Text  = change.ChangeStatus;
                textInvoiceNumber.Text = change.InvoiceNumber;
                textPrivateKey.Text    = Properties.Settings.Default.HidePrivateKeys ? "..." : $"{changeKey.PrivateKey.GetWif(tcBitcoin.GetNetwork)}";;
                textAddress.Text       = $"{changeKey.PrivateKey.GetWif(tcBitcoin.GetNetwork).GetAddress(ScriptPubKeyType.Legacy)}";
                textBalance.Text       = $"{change.Balance}";
                textNote.Text          = change.Note;

                dgTransactions.ItemsSource = tcBitcoin.NodeCash.fnChangeTx(textAddress.Text)
                                             .OrderByDescending(text => text.TransactedOn)
                                             .Select(text => text);
            }
            catch (Exception err)
            {
                MessageBox.Show($"{err.Message}", $"{err.Source}.{err.TargetSite.Name}", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
Ejemplo n.º 21
0
        private ExtKey GetMasterKey()
        {
            if (String.IsNullOrEmpty(WalletExtendedPublicKey))
            {
                return(ExtKey.Parse(WalletPrivateKey));
            }

            var xpub     = ExtPubKey.Parse(WalletExtendedPublicKey);
            var password = default(SecureString);

            try
            {
                var encrypted = BitcoinEncryptedSecret.Create(WalletPrivateKey);
                password = GetPassword();

                return(new ExtKey(xpub, encrypted.GetKey(new NetworkCredential(String.Empty, password).Password)));
            }
            catch (FormatException)
            {
                return(new ExtKey(xpub, Key.Parse(WalletPrivateKey)));
            }
            catch (SecurityException)
            {
                Console.WriteLine("Invalid password.");
#if DEBUG
                Console.WriteLine("Press Any Key To Exit...");
                Console.Read();
#endif
                Environment.Exit(1);
                return(null);
            }
            finally
            {
                password?.Dispose();
            }
        }
        /// <inheritdoc />
        public ISecret GetKeyForAddress(string walletName, string password, HdAddress address)
        {
            Guard.NotEmpty(walletName, nameof(walletName));
            Guard.NotEmpty(password, nameof(password));
            Guard.NotNull(address, nameof(address));

            var wallet = this.GetWalletByName(walletName);

            // check if the wallet contains the address.
            if (!wallet.AccountsRoot.Any(r => r.Accounts.Any(
                                             a => a.ExternalAddresses.Any(i => i.Address == address.Address) ||
                                             a.InternalAddresses.Any(i => i.Address == address.Address))))
            {
                throw new WalletException("Address not found on wallet.");
            }

            // get extended private key
            var           privateKey        = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);
            var           seedExtKey        = new ExtKey(privateKey, wallet.ChainCode);
            ExtKey        addressExtKey     = seedExtKey.Derive(new KeyPath(address.HdPath));
            BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network);

            return(addressPrivateKey);
        }
Ejemplo n.º 23
0
        /// <inheritdoc />
        public Mnemonic CreateWallet(string password, string folderPath, string name, string network, string passphrase = null)
        {
            // for now the passphrase is set to be the password by default.
            if (passphrase == null)
            {
                passphrase = password;
            }

            // generate the root seed used to generate keys from a mnemonic picked at random
            // and a passphrase optionally provided by the user
            Mnemonic mnemonic    = new Mnemonic(Wordlist.English, WordCount.Twelve);
            ExtKey   extendedKey = mnemonic.DeriveExtKey(passphrase);

            Network coinNetwork = WalletHelpers.GetNetwork(network);

            // create a wallet file
            Wallet wallet = this.GenerateWalletFile(password, folderPath, name, coinNetwork, extendedKey);

            // generate multiple accounts and addresses from the get-go
            for (int i = 0; i < WalletCreationAccountsCount; i++)
            {
                HdAccount account = CreateNewAccount(wallet, this.coinType, password);
                this.CreateAddressesInAccount(account, coinNetwork, UnusedAddressesBuffer);
                this.CreateAddressesInAccount(account, coinNetwork, UnusedAddressesBuffer, true);
            }

            // update the height of the we start syncing from
            this.UpdateLastBlockSyncedHeight(wallet, this.chain.Tip.Height);

            // save the changes to the file and add addresses to be tracked
            this.SaveToFile(wallet);
            this.Load(wallet);
            this.LoadKeysLookup();

            return(mnemonic);
        }
Ejemplo n.º 24
0
        public static ExtKey GetMasterExtKey(KeyManager keyManager, string password, out string compatiblityPassword)
        {
            password = Helpers.Guard.Correct(password);             // Correct the password to ensure compatiblity. User will be notified about this through TogglePasswordBox.

            Guard(password);

            compatiblityPassword = null;

            Exception resultException = null;

            foreach (var pw in GetPossiblePasswords(password))
            {
                try
                {
                    ExtKey result = keyManager.GetMasterExtKey(pw);

                    if (resultException != null)                     // Now the password is OK but if we had SecurityException before than we used a cmp password.
                    {
                        compatiblityPassword = pw;
                        Logger.LogError(CompatibilityPasswordWarnMessage);
                    }
                    return(result);
                }
                catch (SecurityException ex)                 // Any other exception - let it go.
                {
                    resultException = ex;
                }
            }

            if (resultException is null)             // This mustn't be null.
            {
                throw new InvalidOperationException();
            }

            throw resultException;             // Throw the last exception - Invalid password.
        }
        internal string SignSGAMessageWithCode(int[] SecretSalt, int[] SecretPW, string sgaMessage, string DisplayCode)
        {
            try
            {
                ExtKey masterKey = GetExtKey(SecretSalt, SecretPW);

                ExtKey masterKeyD = masterKey.Derive(GetCurrencyIndex("btc"), hardened: true);
                ExtKey key        = masterKeyD.Derive((uint)0);
                byte[] keybytes   = key.PrivateKey.ToBytes();



                Key pkey = new Key(keybytes, -1, false);

                BitcoinSecret bitcoinSecret = new BitcoinSecret(pkey, Network.Main);

                var address = bitcoinSecret.PubKey.GetAddress(GetBLKNetworkAlt("btc")).ToString();

                var signature = bitcoinSecret.PrivateKey.SignMessage(sgaMessage);


                Sclear.EraseBytes(keybytes);

                masterKeyD = null;

                key  = null;
                pkey = null;

                NWS WS = new NWS();
                return(WS.GetSGATokenForSignin(address, signature, DisplayCode));
            }
            catch
            {
                return("4");
            }
        }
Ejemplo n.º 26
0
        public void MoreWalletTest()
        {
            Wallet wallet    = new Wallet("赛 烂 肉 什 状 系 既 株 炼 硫 辞 州", "123456");
            var    pk        = wallet.GetPrivateKey(0).ToHex();       //ef47fca84122c17bc312d44985ebf75cd09b4beb611204b43f9f448c86cdf5e3
            var    addrArray = wallet.GetAddresses(19);               //获得20个地址,每一个都不一样,19 是下标

            var masterKey = wallet.GetMasterKey().PrivateKey.ToHex(); //获得主钥
            var publicKey = wallet.GetMasterKey().Neuter();

            int index   = 0;
            var keyPath = new NBitcoin.KeyPath(wallet.Path.Replace("x", index.ToString()));
            // masterKey.Derive(keyPath);

            var    childKey  = wallet.GetMasterKey().Derive((uint)index);
            var    pubKey    = wallet.GetMasterKey().Neuter();
            ExtKey recovered = childKey.GetParentExtKey(pubKey);

            ExtKey    recovered0          = wallet.GetPrivateExtKey(0).Derive(0); //得到序列为0的钱包私钥,计算它的子私钥 对应序列也是0
            ExtPubKey recovered0PublicKey = wallet.GetPrivateExtKey(0).Neuter();  //得到序列为0的钱包私钥 的公钥
            //var result = recovered.PrivateKey.ToHex();
            var recovered0PrivateKey = recovered0.GetParentExtKey(recovered0PublicKey);

            Assert.Equal(wallet.GetPrivateExtKey(0).PrivateKey.ToHex(), recovered0PrivateKey.PrivateKey.ToHex());
        }
Ejemplo n.º 27
0
        public KeyManager(BitcoinEncryptedSecretNoEC encryptedSecret, byte[] chainCode, string password, string filePath = null)
        {
            HdPubKeys               = new List <HdPubKey>();
            HdPubKeyScriptBytes     = new List <byte[]>();
            ScriptHdPubkeyMap       = new Dictionary <Script, HdPubKey>();
            HdPubKeysLock           = new object();
            HdPubKeyScriptBytesLock = new object();
            ScriptHdPubkeyMapLock   = new object();

            if (password is null)
            {
                password = "";
            }

            EncryptedSecret = Guard.NotNull(nameof(encryptedSecret), encryptedSecret);
            ChainCode       = Guard.NotNull(nameof(chainCode), chainCode);
            var extKey = new ExtKey(encryptedSecret.GetKey(password), chainCode);

            ExtPubKey = extKey.Derive(AccountKeyPath).Neuter();

            SetFilePath(filePath);
            ToFileLock = new object();
            ToFile();
        }
Ejemplo n.º 28
0
		public void CanRecoverExtKeyFromExtPubKeyAndOneChildExtKey2()
		{
			for(int i = 0 ; i < 255 ; i++)
			{
				ExtKey key = new ExtKey().Derive((uint)i);
				var childKey = key.Derive((uint)i);
				var pubKey = key.Neuter();
				ExtKey recovered = childKey.GetParentExtKey(pubKey);
				Assert.Equal(recovered.ToString(Network.Main), key.ToString(Network.Main));
			}
		}
Ejemplo n.º 29
0
		public BitcoinExtKey(ExtKey key, Network network)
			: base(key, network)
		{

		}
Ejemplo n.º 30
0
		public void CanSyncWallet2()
		{
			using(NodeServerTester servers = new NodeServerTester(Network.TestNet))
			{
				var chainBuilder = new BlockchainBuilder();
				SetupSPVBehavior(servers, chainBuilder);
				NodesGroup aliceConnection = CreateGroup(servers, 1);
				NodesGroup bobConnection = CreateGroup(servers, 1);

				var aliceKey = new ExtKey();
				Wallet alice = new Wallet(new WalletCreation()
				{
					Network = Network.TestNet,
					RootKeys = new[] { aliceKey.Neuter() },
					SignatureRequired = 1,
					UseP2SH = false
				}, 11);
				Wallet bob = new Wallet(new WalletCreation()
				{
					Network = Network.TestNet,
					RootKeys = new[] { new ExtKey().Neuter() },
					SignatureRequired = 1,
					UseP2SH = false
				}, 11);

				alice.Configure(aliceConnection);
				alice.Connect();

				bob.Configure(bobConnection);
				bob.Connect();

				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1);

				//New address tracked
				var addressAlice = alice.GetNextScriptPubKey();
				chainBuilder.GiveMoney(addressAlice, Money.Coins(1.0m));
				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 0); //Purge
				TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //Reconnect
				//////

				TestUtils.Eventually(() => alice.GetTransactions().Count == 1);

				//Alice send tx to bob
				var coins = alice.GetTransactions().GetSpendableCoins();
				var keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
								.Select(k => aliceKey.Derive(k))
								.ToArray();
				var builder = new TransactionBuilder();
				var tx =
					builder
					.SetTransactionPolicy(new Policy.StandardTransactionPolicy()
					{
						MinRelayTxFee = new FeeRate(0)
					})
					.AddCoins(coins)
					.AddKeys(keys)
					.Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m))
					.SetChange(alice.GetNextScriptPubKey(true))
					.BuildTransaction(true);

				Assert.True(builder.Verify(tx));

				chainBuilder.BroadcastTransaction(tx);

				//Alice get change
				TestUtils.Eventually(() => alice.GetTransactions().Count == 2);
				coins = alice.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.6m));
				//////

				//Bob get coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
				coins = bob.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.4m));
				//////


				MemoryStream bobWalletBackup = new MemoryStream();
				bob.Save(bobWalletBackup);
				bobWalletBackup.Position = 0;

				MemoryStream bobTrakerBackup = new MemoryStream();
				bob.Tracker.Save(bobTrakerBackup);
				bobTrakerBackup.Position = 0;

				bob.Disconnect();

				//Restore bob
				bob = Wallet.Load(bobWalletBackup);
				bobConnection.NodeConnectionParameters.TemplateBehaviors.Remove<TrackerBehavior>();
				bobConnection.NodeConnectionParameters.TemplateBehaviors.Add(new TrackerBehavior(Tracker.Load(bobTrakerBackup), chainBuilder.Chain));
				/////

				bob.Configure(bobConnection);

				//Bob still has coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 1);
				coins = bob.GetTransactions().GetSpendableCoins();
				Assert.True(coins.Single().Amount == Money.Coins(0.4m));
				//////

				bob.Connect();
				TestUtils.Eventually(() => bobConnection.ConnectedNodes.Count == 1);

				//New block found !
				chainBuilder.FindBlock();

				//Alice send tx to bob
				coins = alice.GetTransactions().GetSpendableCoins();
				keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey))
								.Select(k => aliceKey.Derive(k))
								.ToArray();
				builder = new TransactionBuilder();
				tx =
					builder
					.SetTransactionPolicy(new Policy.StandardTransactionPolicy()
					{
						MinRelayTxFee = new FeeRate(0)
					})
					.AddCoins(coins)
					.AddKeys(keys)
					.Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m))
					.SetChange(alice.GetNextScriptPubKey(true))
					.BuildTransaction(true);

				Assert.True(builder.Verify(tx));

				chainBuilder.BroadcastTransaction(tx);

				//Bob still has coins
				TestUtils.Eventually(() => bob.GetTransactions().Count == 2); //Bob has both, old and new tx
				coins = bob.GetTransactions().GetSpendableCoins();
				//////
			}
		}
Ejemplo n.º 31
0
		public void CheckBIP32Constructors()
		{
			var key = new ExtKey();
			Assert.Equal(key.GetWif(Network.Main), new ExtKey(key.PrivateKey, key.ChainCode).GetWif(Network.Main));
			Assert.Equal(key.Neuter().GetWif(Network.Main), new ExtPubKey(key.PrivateKey.PubKey, key.ChainCode).GetWif(Network.Main));

			key = key.Derive(1);
			Assert.Equal(key.GetWif(Network.Main), new ExtKey(key.PrivateKey, key.ChainCode, key.Depth, key.Fingerprint, key.Child).GetWif(Network.Main));
			Assert.Equal(key.Neuter().GetWif(Network.Main), new ExtPubKey(key.PrivateKey.PubKey, key.ChainCode, key.Depth, key.Fingerprint, key.Child).GetWif(Network.Main));
		}
Ejemplo n.º 32
0
        public async Task <IActionResult> Submit(string cryptoCode)
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (network == null)
            {
                return(BadRequest(CreatePayjoinError(400, "invalid-network", "Incorrect network")));
            }

            var explorer = _explorerClientProvider.GetExplorerClient(network);

            if (Request.ContentLength is long length)
            {
                if (length > 1_000_000)
                {
                    return(this.StatusCode(413,
                                           CreatePayjoinError(413, "payload-too-large", "The transaction is too big to be processed")));
                }
            }
            else
            {
                return(StatusCode(411,
                                  CreatePayjoinError(411, "missing-content-length",
                                                     "The http header Content-Length should be filled")));
            }

            string rawBody;

            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                rawBody = (await reader.ReadToEndAsync()) ?? string.Empty;
            }

            Transaction originalTx      = null;
            FeeRate     originalFeeRate = null;
            bool        psbtFormat      = true;

            if (!PSBT.TryParse(rawBody, network.NBitcoinNetwork, out var psbt))
            {
                psbtFormat = false;
                if (!Transaction.TryParse(rawBody, network.NBitcoinNetwork, out var tx))
                {
                    return(BadRequest(CreatePayjoinError(400, "invalid-format", "invalid transaction or psbt")));
                }
                originalTx = tx;
                psbt       = PSBT.FromTransaction(tx, network.NBitcoinNetwork);
                psbt       = (await explorer.UpdatePSBTAsync(new UpdatePSBTRequest()
                {
                    PSBT = psbt
                })).PSBT;
                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    psbt.Inputs[i].FinalScriptSig     = tx.Inputs[i].ScriptSig;
                    psbt.Inputs[i].FinalScriptWitness = tx.Inputs[i].WitScript;
                }
            }
            else
            {
                if (!psbt.IsAllFinalized())
                {
                    return(BadRequest(CreatePayjoinError(400, "psbt-not-finalized", "The PSBT should be finalized")));
                }
                originalTx = psbt.ExtractTransaction();
            }

            async Task BroadcastNow()
            {
                await _explorerClientProvider.GetExplorerClient(network).BroadcastAsync(originalTx);
            }

            var sendersInputType = psbt.GetInputsScriptPubKeyType();

            if (sendersInputType is null)
            {
                return(BadRequest(CreatePayjoinError(400, "unsupported-inputs", "Payjoin only support segwit inputs (of the same type)")));
            }
            if (psbt.CheckSanity() is var errors && errors.Count != 0)
            {
                return(BadRequest(CreatePayjoinError(400, "insane-psbt", $"This PSBT is insane ({errors[0]})")));
            }
            if (!psbt.TryGetEstimatedFeeRate(out originalFeeRate))
            {
                return(BadRequest(CreatePayjoinError(400, "need-utxo-information",
                                                     "You need to provide Witness UTXO information to the PSBT.")));
            }

            // This is actually not a mandatory check, but we don't want implementers
            // to leak global xpubs
            if (psbt.GlobalXPubs.Any())
            {
                return(BadRequest(CreatePayjoinError(400, "leaking-data",
                                                     "GlobalXPubs should not be included in the PSBT")));
            }

            if (psbt.Outputs.Any(o => o.HDKeyPaths.Count != 0) || psbt.Inputs.Any(o => o.HDKeyPaths.Count != 0))
            {
                return(BadRequest(CreatePayjoinError(400, "leaking-data",
                                                     "Keypath information should not be included in the PSBT")));
            }

            if (psbt.Inputs.Any(o => !o.IsFinalized()))
            {
                return(BadRequest(CreatePayjoinError(400, "psbt-not-finalized", "The PSBT Should be finalized")));
            }
            ////////////

            var mempool = await explorer.BroadcastAsync(originalTx, true);

            if (!mempool.Success)
            {
                return(BadRequest(CreatePayjoinError(400, "invalid-transaction",
                                                     $"Provided transaction isn't mempool eligible {mempool.RPCCodeMessage}")));
            }

            var   paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            bool  paidSomething   = false;
            Money due             = null;
            Dictionary <OutPoint, UTXO> selectedUTXOs = new Dictionary <OutPoint, UTXO>();

            async Task UnlockUTXOs()
            {
                await _payJoinRepository.TryUnlock(selectedUTXOs.Select(o => o.Key).ToArray());
            }

            PSBTOutput               originalPaymentOutput = null;
            BitcoinAddress           paymentAddress        = null;
            InvoiceEntity            invoice = null;
            DerivationSchemeSettings derivationSchemeSettings = null;

            foreach (var output in psbt.Outputs)
            {
                var key = output.ScriptPubKey.Hash + "#" + network.CryptoCode.ToUpperInvariant();
                invoice = (await _invoiceRepository.GetInvoicesFromAddresses(new[] { key })).FirstOrDefault();
                if (invoice is null)
                {
                    continue;
                }
                derivationSchemeSettings = invoice.GetSupportedPaymentMethod <DerivationSchemeSettings>(paymentMethodId)
                                           .SingleOrDefault();
                if (derivationSchemeSettings is null)
                {
                    continue;
                }

                var receiverInputsType = derivationSchemeSettings.AccountDerivation.ScriptPubKeyType();
                if (!PayjoinClient.SupportedFormats.Contains(receiverInputsType))
                {
                    //this should never happen, unless the store owner changed the wallet mid way through an invoice
                    return(StatusCode(500, CreatePayjoinError(500, "unavailable", $"This service is unavailable for now")));
                }
                if (sendersInputType != receiverInputsType)
                {
                    return(StatusCode(503,
                                      CreatePayjoinError(503, "out-of-utxos",
                                                         "We do not have any UTXO available for making a payjoin with the sender's inputs type")));
                }
                var paymentMethod  = invoice.GetPaymentMethod(paymentMethodId);
                var paymentDetails =
                    paymentMethod.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
                if (paymentDetails is null || !paymentDetails.PayjoinEnabled)
                {
                    continue;
                }
                if (invoice.GetAllBitcoinPaymentData().Any())
                {
                    return(UnprocessableEntity(CreatePayjoinError(422, "already-paid",
                                                                  $"The invoice this PSBT is paying has already been partially or completely paid")));
                }

                paidSomething = true;
                due           = paymentMethod.Calculate().TotalDue - output.Value;
                if (due > Money.Zero)
                {
                    break;
                }

                if (!await _payJoinRepository.TryLockInputs(originalTx.Inputs.Select(i => i.PrevOut).ToArray()))
                {
                    return(BadRequest(CreatePayjoinError(400, "inputs-already-used",
                                                         "Some of those inputs have already been used to make payjoin transaction")));
                }

                var utxos = (await explorer.GetUTXOsAsync(derivationSchemeSettings.AccountDerivation))
                            .GetUnspentUTXOs(false);
                // In case we are paying ourselves, be need to make sure
                // we can't take spent outpoints.
                var prevOuts = originalTx.Inputs.Select(o => o.PrevOut).ToHashSet();
                utxos = utxos.Where(u => !prevOuts.Contains(u.Outpoint)).ToArray();
                Array.Sort(utxos, UTXODeterministicComparer.Instance);
                foreach (var utxo in await SelectUTXO(network, utxos, output.Value,
                                                      psbt.Outputs.Where(o => o.Index != output.Index).Select(o => o.Value).ToArray()))
                {
                    selectedUTXOs.Add(utxo.Outpoint, utxo);
                }

                originalPaymentOutput = output;
                paymentAddress        = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
                break;
            }

            if (!paidSomething)
            {
                return(BadRequest(CreatePayjoinError(400, "invoice-not-found",
                                                     "This transaction does not pay any invoice with payjoin")));
            }

            if (due is null || due > Money.Zero)
            {
                return(BadRequest(CreatePayjoinError(400, "invoice-not-fully-paid",
                                                     "The transaction must pay the whole invoice")));
            }

            if (selectedUTXOs.Count == 0)
            {
                await BroadcastNow();

                return(StatusCode(503,
                                  CreatePayjoinError(503, "out-of-utxos",
                                                     "We do not have any UTXO available for making a payjoin for now")));
            }

            var originalPaymentValue = originalPaymentOutput.Value;
            await _broadcaster.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromMinutes(1.0), originalTx, network);

            //check if wallet of store is configured to be hot wallet
            var extKeyStr = await explorer.GetMetadataAsync <string>(
                derivationSchemeSettings.AccountDerivation,
                WellknownMetadataKeys.AccountHDKey);

            if (extKeyStr == null)
            {
                // This should not happen, as we check the existance of private key before creating invoice with payjoin
                await UnlockUTXOs();
                await BroadcastNow();

                return(StatusCode(500, CreatePayjoinError(500, "unavailable", $"This service is unavailable for now")));
            }

            Money           contributedAmount = Money.Zero;
            var             newTx             = originalTx.Clone();
            var             ourNewOutput      = newTx.Outputs[originalPaymentOutput.Index];
            HashSet <TxOut> isOurOutput       = new HashSet <TxOut>();

            isOurOutput.Add(ourNewOutput);
            foreach (var selectedUTXO in selectedUTXOs.Select(o => o.Value))
            {
                contributedAmount += (Money)selectedUTXO.Value;
                newTx.Inputs.Add(selectedUTXO.Outpoint);
            }
            ourNewOutput.Value += contributedAmount;
            var minRelayTxFee = this._dashboard.Get(network.CryptoCode).Status.BitcoinStatus?.MinRelayTxFee ??
                                new FeeRate(1.0m);

            // Probably receiving some spare change, let's add an output to make
            // it looks more like a normal transaction
            if (newTx.Outputs.Count == 1)
            {
                var change = await explorer.GetUnusedAsync(derivationSchemeSettings.AccountDerivation, DerivationFeature.Change);

                var randomChangeAmount = RandomUtils.GetUInt64() % (ulong)contributedAmount.Satoshi;

                // Randomly round the amount to make the payment output look like a change output
                var roundMultiple = (ulong)Math.Pow(10, (ulong)Math.Log10(randomChangeAmount));
                while (roundMultiple > 1_000UL)
                {
                    if (RandomUtils.GetUInt32() % 2 == 0)
                    {
                        roundMultiple = roundMultiple / 10;
                    }
                    else
                    {
                        randomChangeAmount = (randomChangeAmount / roundMultiple) * roundMultiple;
                        break;
                    }
                }

                var fakeChange = newTx.Outputs.CreateNewTxOut(randomChangeAmount, change.ScriptPubKey);
                if (fakeChange.IsDust(minRelayTxFee))
                {
                    randomChangeAmount = fakeChange.GetDustThreshold(minRelayTxFee);
                    fakeChange.Value   = randomChangeAmount;
                }
                if (randomChangeAmount < contributedAmount)
                {
                    ourNewOutput.Value -= fakeChange.Value;
                    newTx.Outputs.Add(fakeChange);
                    isOurOutput.Add(fakeChange);
                }
            }

            var rand = new Random();

            Utils.Shuffle(newTx.Inputs, rand);
            Utils.Shuffle(newTx.Outputs, rand);

            // Remove old signatures as they are not valid anymore
            foreach (var input in newTx.Inputs)
            {
                input.WitScript = WitScript.Empty;
            }

            Money ourFeeContribution = Money.Zero;
            // We need to adjust the fee to keep a constant fee rate
            var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder();

            txBuilder.AddCoins(psbt.Inputs.Select(i => i.GetSignableCoin()));
            txBuilder.AddCoins(selectedUTXOs.Select(o => o.Value.AsCoin(derivationSchemeSettings.AccountDerivation)));
            Money expectedFee   = txBuilder.EstimateFees(newTx, originalFeeRate);
            Money actualFee     = newTx.GetFee(txBuilder.FindSpentCoins(newTx));
            Money additionalFee = expectedFee - actualFee;

            if (additionalFee > Money.Zero)
            {
                // If the user overpaid, taking fee on our output (useful if sender dump a full UTXO for privacy)
                for (int i = 0; i < newTx.Outputs.Count && additionalFee > Money.Zero && due < Money.Zero; i++)
                {
                    if (isOurOutput.Contains(newTx.Outputs[i]))
                    {
                        var outputContribution = Money.Min(additionalFee, -due);
                        outputContribution = Money.Min(outputContribution,
                                                       newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold(minRelayTxFee));
                        newTx.Outputs[i].Value -= outputContribution;
                        additionalFee          -= outputContribution;
                        due += outputContribution;
                        ourFeeContribution += outputContribution;
                    }
                }

                // The rest, we take from user's change
                for (int i = 0; i < newTx.Outputs.Count && additionalFee > Money.Zero; i++)
                {
                    if (!isOurOutput.Contains(newTx.Outputs[i]))
                    {
                        var outputContribution = Money.Min(additionalFee, newTx.Outputs[i].Value);
                        outputContribution = Money.Min(outputContribution,
                                                       newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold(minRelayTxFee));
                        newTx.Outputs[i].Value -= outputContribution;
                        additionalFee          -= outputContribution;
                    }
                }

                if (additionalFee > Money.Zero)
                {
                    // We could not pay fully the additional fee, however, as long as
                    // we are not under the relay fee, it should be OK.
                    var newVSize   = txBuilder.EstimateSize(newTx, true);
                    var newFeePaid = newTx.GetFee(txBuilder.FindSpentCoins(newTx));
                    if (new FeeRate(newFeePaid, newVSize) < minRelayTxFee)
                    {
                        await UnlockUTXOs();
                        await BroadcastNow();

                        return(UnprocessableEntity(CreatePayjoinError(422, "not-enough-money",
                                                                      "Not enough money is sent to pay for the additional payjoin inputs")));
                    }
                }
            }

            var accountKey = ExtKey.Parse(extKeyStr, network.NBitcoinNetwork);
            var newPsbt    = PSBT.FromTransaction(newTx, network.NBitcoinNetwork);

            foreach (var selectedUtxo in selectedUTXOs.Select(o => o.Value))
            {
                var signedInput = newPsbt.Inputs.FindIndexedInput(selectedUtxo.Outpoint);
                var coin        = selectedUtxo.AsCoin(derivationSchemeSettings.AccountDerivation);
                signedInput.UpdateFromCoin(coin);
                var privateKey = accountKey.Derive(selectedUtxo.KeyPath).PrivateKey;
                signedInput.Sign(privateKey);
                signedInput.FinalizeInput();
                newTx.Inputs[signedInput.Index].WitScript = newPsbt.Inputs[(int)signedInput.Index].FinalScriptWitness;
            }

            // Add the transaction to the payments with a confirmation of -1.
            // This will make the invoice paid even if the user do not
            // broadcast the payjoin.
            var originalPaymentData = new BitcoinLikePaymentData(paymentAddress,
                                                                 originalPaymentOutput.Value,
                                                                 new OutPoint(originalTx.GetHash(), originalPaymentOutput.Index),
                                                                 originalTx.RBF);

            originalPaymentData.ConfirmationCount  = -1;
            originalPaymentData.PayjoinInformation = new PayjoinInformation()
            {
                CoinjoinTransactionHash = newPsbt.GetGlobalTransaction().GetHash(),
                CoinjoinValue           = originalPaymentValue - ourFeeContribution,
                ContributedOutPoints    = selectedUTXOs.Select(o => o.Key).ToArray()
            };
            var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);

            if (payment is null)
            {
                await UnlockUTXOs();
                await BroadcastNow();

                return(UnprocessableEntity(CreatePayjoinError(422, "already-paid",
                                                              $"The original transaction has already been accounted")));
            }
            await _btcPayWalletProvider.GetWallet(network).SaveOffchainTransactionAsync(originalTx);

            _eventAggregator.Publish(new InvoiceEvent(invoice, 1002, InvoiceEvent.ReceivedPayment)
            {
                Payment = payment
            });

            if (psbtFormat && HexEncoder.IsWellFormed(rawBody))
            {
                return(Ok(newPsbt.ToHex()));
            }
            else if (psbtFormat)
            {
                return(Ok(newPsbt.ToBase64()));
            }
            else
            {
                return(Ok(newTx.ToHex()));
            }
        }
Ejemplo n.º 33
0
		public void CanRecoverExtKeyFromExtPubKeyAndSecret()
		{
			ExtKey key = new ExtKey().Derive(1);
			var underlying = key.PrivateKey.GetBitcoinSecret(Network.Main);
			var pubKey = key.Neuter().GetWif(Network.Main);
			ExtKey key2 = new ExtKey(pubKey, underlying);
			Assert.Equal(key.ToString(Network.Main), key2.ToString(Network.Main));
		}
Ejemplo n.º 34
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);
		}
Ejemplo n.º 35
0
        public async Task <IActionResult> Submit(string cryptoCode,
                                                 long?maxadditionalfeecontribution,
                                                 int?additionalfeeoutputindex,
                                                 decimal minfeerate             = -1.0m,
                                                 bool disableoutputsubstitution = false,
                                                 int v = 1)
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (network == null)
            {
                return(NotFound());
            }

            if (v != 1)
            {
                return(BadRequest(new JObject
                {
                    new JProperty("errorCode", "version-unsupported"),
                    new JProperty("supported", new JArray(1)),
                    new JProperty("message", "This version of payjoin is not supported.")
                }));
            }

            await using var ctx = new PayjoinReceiverContext(_invoiceRepository, _explorerClientProvider.GetExplorerClient(network), _payJoinRepository);
            ObjectResult CreatePayjoinErrorAndLog(int httpCode, PayjoinReceiverWellknownErrors err, string debug)
            {
                ctx.Logs.Write($"Payjoin error: {debug}");
                return(StatusCode(httpCode, CreatePayjoinError(err, debug)));
            }

            var explorer = _explorerClientProvider.GetExplorerClient(network);

            if (Request.ContentLength is long length)
            {
                if (length > 1_000_000)
                {
                    return(this.StatusCode(413,
                                           CreatePayjoinError("payload-too-large", "The transaction is too big to be processed")));
                }
            }
            else
            {
                return(StatusCode(411,
                                  CreatePayjoinError("missing-content-length",
                                                     "The http header Content-Length should be filled")));
            }

            string rawBody;

            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                rawBody = (await reader.ReadToEndAsync()) ?? string.Empty;
            }

            FeeRate originalFeeRate = null;
            bool    psbtFormat      = true;

            if (PSBT.TryParse(rawBody, network.NBitcoinNetwork, out var psbt))
            {
                if (!psbt.IsAllFinalized())
                {
                    return(BadRequest(CreatePayjoinError("original-psbt-rejected", "The PSBT should be finalized")));
                }
                ctx.OriginalTransaction = psbt.ExtractTransaction();
            }
            // BTCPay Server implementation support a transaction instead of PSBT
            else
            {
                psbtFormat = false;
                if (!Transaction.TryParse(rawBody, network.NBitcoinNetwork, out var tx))
                {
                    return(BadRequest(CreatePayjoinError("original-psbt-rejected", "invalid transaction or psbt")));
                }
                ctx.OriginalTransaction = tx;
                psbt = PSBT.FromTransaction(tx, network.NBitcoinNetwork);
                psbt = (await explorer.UpdatePSBTAsync(new UpdatePSBTRequest()
                {
                    PSBT = psbt
                })).PSBT;
                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    psbt.Inputs[i].FinalScriptSig     = tx.Inputs[i].ScriptSig;
                    psbt.Inputs[i].FinalScriptWitness = tx.Inputs[i].WitScript;
                }
            }

            FeeRate senderMinFeeRate             = minfeerate >= 0.0m ? new FeeRate(minfeerate) : null;
            Money   allowedSenderFeeContribution = Money.Satoshis(maxadditionalfeecontribution is long t && t >= 0 ? t : 0);

            var sendersInputType = psbt.GetInputsScriptPubKeyType();

            if (psbt.CheckSanity() is var errors && errors.Count != 0)
            {
                return(BadRequest(CreatePayjoinError("original-psbt-rejected", $"This PSBT is insane ({errors[0]})")));
            }
            if (!psbt.TryGetEstimatedFeeRate(out originalFeeRate))
            {
                return(BadRequest(CreatePayjoinError("original-psbt-rejected",
                                                     "You need to provide Witness UTXO information to the PSBT.")));
            }

            // This is actually not a mandatory check, but we don't want implementers
            // to leak global xpubs
            if (psbt.GlobalXPubs.Any())
            {
                return(BadRequest(CreatePayjoinError("original-psbt-rejected",
                                                     "GlobalXPubs should not be included in the PSBT")));
            }

            if (psbt.Outputs.Any(o => o.HDKeyPaths.Count != 0) || psbt.Inputs.Any(o => o.HDKeyPaths.Count != 0))
            {
                return(BadRequest(CreatePayjoinError("original-psbt-rejected",
                                                     "Keypath information should not be included in the PSBT")));
            }

            if (psbt.Inputs.Any(o => !o.IsFinalized()))
            {
                return(BadRequest(CreatePayjoinError("original-psbt-rejected", "The PSBT Should be finalized")));
            }
            ////////////

            var mempool = await explorer.BroadcastAsync(ctx.OriginalTransaction, true);

            if (!mempool.Success)
            {
                ctx.DoNotBroadcast();
                return(BadRequest(CreatePayjoinError("original-psbt-rejected",
                                                     $"Provided transaction isn't mempool eligible {mempool.RPCCodeMessage}")));
            }
            var   enforcedLowR    = ctx.OriginalTransaction.Inputs.All(IsLowR);
            var   paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            bool  paidSomething   = false;
            Money due             = null;
            Dictionary <OutPoint, UTXO> selectedUTXOs      = new Dictionary <OutPoint, UTXO>();
            PSBTOutput               originalPaymentOutput = null;
            BitcoinAddress           paymentAddress        = null;
            InvoiceEntity            invoice = null;
            DerivationSchemeSettings derivationSchemeSettings = null;

            foreach (var output in psbt.Outputs)
            {
                var key = output.ScriptPubKey.Hash + "#" + network.CryptoCode.ToUpperInvariant();
                invoice = (await _invoiceRepository.GetInvoicesFromAddresses(new[] { key })).FirstOrDefault();
                if (invoice is null)
                {
                    continue;
                }
                derivationSchemeSettings = invoice.GetSupportedPaymentMethod <DerivationSchemeSettings>(paymentMethodId)
                                           .SingleOrDefault();
                if (derivationSchemeSettings is null)
                {
                    continue;
                }

                var receiverInputsType = derivationSchemeSettings.AccountDerivation.ScriptPubKeyType();
                if (!PayjoinClient.SupportedFormats.Contains(receiverInputsType))
                {
                    //this should never happen, unless the store owner changed the wallet mid way through an invoice
                    return(CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "Our wallet does not support payjoin"));
                }
                if (sendersInputType is ScriptPubKeyType t1 && t1 != receiverInputsType)
                {
                    return(CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "We do not have any UTXO available for making a payjoin with the sender's inputs type"));
                }
                var paymentMethod  = invoice.GetPaymentMethod(paymentMethodId);
                var paymentDetails =
                    paymentMethod.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
                if (paymentDetails is null || !paymentDetails.PayjoinEnabled)
                {
                    continue;
                }
                if (invoice.GetAllBitcoinPaymentData().Any())
                {
                    ctx.DoNotBroadcast();
                    return(UnprocessableEntity(CreatePayjoinError("already-paid",
                                                                  $"The invoice this PSBT is paying has already been partially or completely paid")));
                }

                paidSomething = true;
                due           = paymentMethod.Calculate().TotalDue - output.Value;
                if (due > Money.Zero)
                {
                    break;
                }

                if (!await _payJoinRepository.TryLockInputs(ctx.OriginalTransaction.Inputs.Select(i => i.PrevOut).ToArray()))
                {
                    return(CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "Some of those inputs have already been used to make another payjoin transaction"));
                }

                var utxos = (await explorer.GetUTXOsAsync(derivationSchemeSettings.AccountDerivation))
                            .GetUnspentUTXOs(false);
                // In case we are paying ourselves, be need to make sure
                // we can't take spent outpoints.
                var prevOuts = ctx.OriginalTransaction.Inputs.Select(o => o.PrevOut).ToHashSet();
                utxos = utxos.Where(u => !prevOuts.Contains(u.Outpoint)).ToArray();
                Array.Sort(utxos, UTXODeterministicComparer.Instance);
                foreach (var utxo in (await SelectUTXO(network, utxos, psbt.Inputs.Select(input => input.WitnessUtxo.Value.ToDecimal(MoneyUnit.BTC)), output.Value.ToDecimal(MoneyUnit.BTC),
                                                       psbt.Outputs.Where(psbtOutput => psbtOutput.Index != output.Index).Select(psbtOutput => psbtOutput.Value.ToDecimal(MoneyUnit.BTC)))).selectedUTXO)
                {
                    selectedUTXOs.Add(utxo.Outpoint, utxo);
                }
                ctx.LockedUTXOs       = selectedUTXOs.Select(u => u.Key).ToArray();
                originalPaymentOutput = output;
                paymentAddress        = paymentDetails.GetDepositAddress(network.NBitcoinNetwork);
                break;
            }

            if (!paidSomething)
            {
                return(BadRequest(CreatePayjoinError("invoice-not-found",
                                                     "This transaction does not pay any invoice with payjoin")));
            }

            if (due is null || due > Money.Zero)
            {
                return(BadRequest(CreatePayjoinError("invoice-not-fully-paid",
                                                     "The transaction must pay the whole invoice")));
            }

            if (selectedUTXOs.Count == 0)
            {
                return(CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "We do not have any UTXO available for contributing to a payjoin"));
            }

            var originalPaymentValue = originalPaymentOutput.Value;
            await _broadcaster.Schedule(DateTimeOffset.UtcNow + TimeSpan.FromMinutes(2.0), ctx.OriginalTransaction, network);

            //check if wallet of store is configured to be hot wallet
            var extKeyStr = await explorer.GetMetadataAsync <string>(
                derivationSchemeSettings.AccountDerivation,
                WellknownMetadataKeys.AccountHDKey);

            if (extKeyStr == null)
            {
                // This should not happen, as we check the existance of private key before creating invoice with payjoin
                return(CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "The HD Key of the store changed"));
            }

            Money           contributedAmount = Money.Zero;
            var             newTx             = ctx.OriginalTransaction.Clone();
            var             ourNewOutput      = newTx.Outputs[originalPaymentOutput.Index];
            HashSet <TxOut> isOurOutput       = new HashSet <TxOut>();

            isOurOutput.Add(ourNewOutput);
            TxOut feeOutput =
                additionalfeeoutputindex is int feeOutputIndex &&
                maxadditionalfeecontribution is long v3 &&
                v3 >= 0 &&
                feeOutputIndex >= 0 &&
                feeOutputIndex < newTx.Outputs.Count &&
                !isOurOutput.Contains(newTx.Outputs[feeOutputIndex])
                ? newTx.Outputs[feeOutputIndex] : null;
            var rand             = new Random();
            int senderInputCount = newTx.Inputs.Count;

            foreach (var selectedUTXO in selectedUTXOs.Select(o => o.Value))
            {
                contributedAmount += (Money)selectedUTXO.Value;
                var newInput = newTx.Inputs.Add(selectedUTXO.Outpoint);
                newInput.Sequence = newTx.Inputs[rand.Next(0, senderInputCount)].Sequence;
            }
            ourNewOutput.Value += contributedAmount;
            var minRelayTxFee = this._dashboard.Get(network.CryptoCode).Status.BitcoinStatus?.MinRelayTxFee ??
                                new FeeRate(1.0m);

            // Remove old signatures as they are not valid anymore
            foreach (var input in newTx.Inputs)
            {
                input.WitScript = WitScript.Empty;
            }

            Money ourFeeContribution = Money.Zero;
            // We need to adjust the fee to keep a constant fee rate
            var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder();
            var coins     = psbt.Inputs.Select(i => i.GetSignableCoin())
                            .Concat(selectedUTXOs.Select(o => o.Value.AsCoin(derivationSchemeSettings.AccountDerivation))).ToArray();

            txBuilder.AddCoins(coins);
            Money expectedFee   = txBuilder.EstimateFees(newTx, originalFeeRate);
            Money actualFee     = newTx.GetFee(txBuilder.FindSpentCoins(newTx));
            Money additionalFee = expectedFee - actualFee;

            if (additionalFee > Money.Zero)
            {
                // If the user overpaid, taking fee on our output (useful if sender dump a full UTXO for privacy)
                for (int i = 0; i < newTx.Outputs.Count && additionalFee > Money.Zero && due < Money.Zero; i++)
                {
                    if (disableoutputsubstitution)
                    {
                        break;
                    }
                    if (isOurOutput.Contains(newTx.Outputs[i]))
                    {
                        var outputContribution = Money.Min(additionalFee, -due);
                        outputContribution = Money.Min(outputContribution,
                                                       newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold(minRelayTxFee));
                        newTx.Outputs[i].Value -= outputContribution;
                        additionalFee          -= outputContribution;
                        due += outputContribution;
                        ourFeeContribution += outputContribution;
                    }
                }

                // The rest, we take from user's change
                if (feeOutput != null)
                {
                    var outputContribution = Money.Min(additionalFee, feeOutput.Value);
                    outputContribution = Money.Min(outputContribution,
                                                   feeOutput.Value - feeOutput.GetDustThreshold(minRelayTxFee));
                    outputContribution            = Money.Min(outputContribution, allowedSenderFeeContribution);
                    feeOutput.Value              -= outputContribution;
                    additionalFee                -= outputContribution;
                    allowedSenderFeeContribution -= outputContribution;
                }

                if (additionalFee > Money.Zero)
                {
                    // We could not pay fully the additional fee, however, as long as
                    // we are not under the relay fee, it should be OK.
                    var newVSize   = txBuilder.EstimateSize(newTx, true);
                    var newFeePaid = newTx.GetFee(txBuilder.FindSpentCoins(newTx));
                    if (new FeeRate(newFeePaid, newVSize) < (senderMinFeeRate ?? minRelayTxFee))
                    {
                        return(CreatePayjoinErrorAndLog(422, PayjoinReceiverWellknownErrors.NotEnoughMoney, "Not enough money is sent to pay for the additional payjoin inputs"));
                    }
                }
            }

            var accountKey = ExtKey.Parse(extKeyStr, network.NBitcoinNetwork);
            var newPsbt    = PSBT.FromTransaction(newTx, network.NBitcoinNetwork);

            foreach (var selectedUtxo in selectedUTXOs.Select(o => o.Value))
            {
                var signedInput = newPsbt.Inputs.FindIndexedInput(selectedUtxo.Outpoint);
                var coin        = selectedUtxo.AsCoin(derivationSchemeSettings.AccountDerivation);
                signedInput.UpdateFromCoin(coin);
                var privateKey = accountKey.Derive(selectedUtxo.KeyPath).PrivateKey;
                signedInput.Sign(privateKey, new SigningOptions()
                {
                    EnforceLowR = enforcedLowR
                });
                signedInput.FinalizeInput();
                newTx.Inputs[signedInput.Index].WitScript = newPsbt.Inputs[(int)signedInput.Index].FinalScriptWitness;
            }

            // Add the transaction to the payments with a confirmation of -1.
            // This will make the invoice paid even if the user do not
            // broadcast the payjoin.
            var originalPaymentData = new BitcoinLikePaymentData(paymentAddress,
                                                                 originalPaymentOutput.Value,
                                                                 new OutPoint(ctx.OriginalTransaction.GetHash(), originalPaymentOutput.Index),
                                                                 ctx.OriginalTransaction.RBF);

            originalPaymentData.ConfirmationCount  = -1;
            originalPaymentData.PayjoinInformation = new PayjoinInformation()
            {
                CoinjoinTransactionHash = GetExpectedHash(newPsbt, coins),
                CoinjoinValue           = originalPaymentValue - ourFeeContribution,
                ContributedOutPoints    = selectedUTXOs.Select(o => o.Key).ToArray()
            };
            var payment = await _invoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, originalPaymentData, network, true);

            if (payment is null)
            {
                return(UnprocessableEntity(CreatePayjoinError("already-paid",
                                                              $"The original transaction has already been accounted")));
            }
            await _btcPayWalletProvider.GetWallet(network).SaveOffchainTransactionAsync(ctx.OriginalTransaction);

            _eventAggregator.Publish(new InvoiceEvent(invoice, 1002, InvoiceEvent.ReceivedPayment)
            {
                Payment = payment
            });
            _eventAggregator.Publish(new UpdateTransactionLabel()
            {
                WalletId          = new WalletId(invoice.StoreId, network.CryptoCode),
                TransactionLabels = selectedUTXOs.GroupBy(pair => pair.Key.Hash).Select(utxo =>
                                                                                        new KeyValuePair <uint256, List <(string color, string label)> >(utxo.Key,
                                                                                                                                                         new List <(string color, string label)>()
                {
                    UpdateTransactionLabel.PayjoinExposedLabelTemplate(invoice.Id)
                }))
                                    .ToDictionary(pair => pair.Key, pair => pair.Value)
            });
Ejemplo n.º 36
0
		public void CanRoundTripExtKeyBase58Data()
		{
			var key = new ExtKey();
			var pubkey = key.Neuter();
			Assert.True(ExtKey.Parse(key.ToString(Network.Main)).ToString(Network.Main) == key.ToString(Network.Main));
			Assert.True(ExtPubKey.Parse(pubkey.ToString(Network.Main)).ToString(Network.Main) == pubkey.ToString(Network.Main));
		}
Ejemplo n.º 37
0
        private void ShowHistory()
        {
            outputTranstactionHistory.Rows.Clear();
            MyFile       myFile       = new MyFile();
            WalletDotDat walletDotDat = new WalletDotDat();
            string       text         = myFile.ReadFile(path);

            string[] splitted = text.Split(' ');
            string   seed;
            int      i;

            if (splitted.Length != 12)
            {
                Aes aes = new Aes();
                walletDotDat.FromString(aes.Decrypt(text, password));
                seed = walletDotDat.mnemonics;
                i    = walletDotDat.bitcoinSecrets.Count;
            }
            else
            {
                walletDotDat.FromString(myFile.ReadFile(path));
                seed = walletDotDat.mnemonics;
                i    = walletDotDat.bitcoinSecrets.Count;
            }

            ExtKey extKey = Wallet.generateMasterAdress(seed);

            int    n       = 0;
            string address = "";

            for (int j = 0; j < i; j++)
            {
                bitcoinSecret = Wallet.generateDerivedAdress(extKey, j);
                address       = bitcoinSecret.PubKey.GetAddress(Network.TestNet).ToString();

                using (var client = new HttpClient())
                {
                    var result = client.GetAsync("http://tapi.qbit.ninja/balances/" + address).Result;
                    if (result.IsSuccessStatusCode)
                    {
                        var     content      = result.Content;
                        string  transactions = content.ReadAsStringAsync().Result;
                        JObject json         = JObject.Parse(transactions);

                        foreach (var transaction in json["operations"])
                        {
                            outputTranstactionHistory.Rows.Add();
                            if (int.Parse(transaction["confirmations"].ToString()) > 6)
                            {
                                outputTranstactionHistory.Rows[n].Cells[0].Value = "6+";
                            }
                            else
                            {
                                outputTranstactionHistory.Rows[n].Cells[0].Value = transaction["confirmations"];
                            }
                            outputTranstactionHistory.Rows[n].Cells[1].Value = transaction["firstSeen"];

                            if (int.Parse(transaction["confirmations"].ToString()) < 6)
                            {
                                outputTranstactionHistory.Rows[n].Cells[2].Value = "Unconfirmed transaction";
                            }
                            else
                            {
                                outputTranstactionHistory.Rows[n].Cells[2].Value = "Confirmed transaction";
                            }
                            outputTranstactionHistory.Rows[n].Cells[3].Value = decimal.Parse(transaction["amount"].ToString()) * 0.00000001m;
                            n++;
                        }
                    }
                }
            }
            if (outputTranstactionHistory.Rows.Count != 0)
            {
                outputTranstactionHistory.Sort(outputTranstactionHistory.Columns[1], ListSortDirection.Descending);
                outputTranstactionHistory.Rows[outputTranstactionHistory.Rows.Count - 1].Cells[4].Value = outputTranstactionHistory.Rows[outputTranstactionHistory.Rows.Count - 1].Cells[3].Value;
                for (int j = outputTranstactionHistory.Rows.Count - 2; j >= 0; j--)
                {
                    outputTranstactionHistory.Rows[j].Cells[4].Value = decimal.Parse(outputTranstactionHistory.Rows[j + 1].Cells[4].Value.ToString()) + decimal.Parse(outputTranstactionHistory.Rows[j].Cells[3].Value.ToString());
                }
            }
        }
Ejemplo n.º 38
0
		public void CanCheckChildKey()
		{
			var parent = new ExtKey();
			var child = parent.Derive(1);
			var notchild = new ExtKey();

			Assert.True(child.IsChildOf(parent));
			Assert.True(parent.IsParentOf(child));
			Assert.False(notchild.IsChildOf(parent));
			Assert.False(parent.IsParentOf(notchild));

			Assert.True(child.Neuter().IsChildOf(parent.Neuter()));
			Assert.True(parent.Neuter().IsParentOf(child.Neuter()));
			Assert.False(notchild.Neuter().IsChildOf(parent.Neuter()));
			Assert.False(parent.Neuter().IsParentOf(notchild.Neuter()));

			var keyA = parent.Neuter();
			var keyB = new ExtPubKey(keyA.ToBytes());
			AssertEx.CollectionEquals(keyA.ToBytes(), keyB.ToBytes());
		}
 public void Execute(object parameter)
 {
     PrivateKey = new ExtKey().GetWif(App.Network);
     PubKey     = PrivateKey.ExtKey.Neuter().GetWif(App.Network);
 }
Ejemplo n.º 40
0
		public void CanCheckChildKey()
		{
			var parent = new ExtKey();
			var child = parent.Derive(1);
			var notchild = new ExtKey();

			Assert.True(child.IsChildOf(parent));
			Assert.True(parent.IsParentOf(child));
			Assert.False(notchild.IsChildOf(parent));
			Assert.False(parent.IsParentOf(notchild));

			Assert.True(child.Neuter().IsChildOf(parent.Neuter()));
			Assert.True(parent.Neuter().IsParentOf(child.Neuter()));
			Assert.False(notchild.Neuter().IsChildOf(parent.Neuter()));
			Assert.False(parent.Neuter().IsParentOf(notchild.Neuter()));
		}