Beispiel #1
0
        public static void issue_burn_tx(
            WalletConfig config,
            Keychain keychain,
            ulong amount,
            ulong minimumConfirmations,
            uint maxOutputs
            )
        {
            keychain = Keychain.Burn_enabled(keychain, Identifier.Zero());

            var chainTip      = Checker.get_tip_from_node(config);
            var currentHeight = chainTip.Height;

            var _ = Checker.refresh_outputs(config, keychain);

            var keyId = keychain.Root_key_id();

// select some spendable coins from the walvar
            var coins = WalletData.Read_wallet(
                config.DataFileDir, walletData => walletData.Select(
                    keyId.Clone(),
                    amount,
                    currentHeight,
                    minimumConfirmations,
                    maxOutputs,
                    false));


            Log.Debug("selected some coins - {}", coins.Length);

            var(partsArray, _) = inputs_and_change(coins, config, keychain, amount);

            var parts = partsArray.ToList();

            // add burn output and fees
            var fee = Types.tx_fee((uint)coins.Length, 2, null);

            parts.Add(c => c.Output(amount - fee, Identifier.Zero()));

            // finalize the burn transaction and send
            var(txBurn, _) = Build.Transaction(parts.ToArray(), keychain);
            txBurn.Validate(keychain.Secp);

            var txHex = HexUtil.to_hex(Ser.Ser_vec(txBurn));

            var url = $"{config.CheckNodeApiHttpAddr}/v1/pool/push";

            var json = JsonConvert.SerializeObject(new TxWrapper {
                TxHex = txHex
            });

            var res = ApiClient.PostContentAsync(url, new StringContent(json, Encoding.UTF8, "application/json")).Result;

            Log.Debug("{StatusCode}", res.StatusCode);
        }
Beispiel #2
0
        // Read wallet data without acquiring the write lock.
        public static (Identifier, uint) retrieve_existing_key(
            WalletConfig config,
            Identifier keyId
            )

        {
            return(WalletData.Read_wallet(config.DataFileDir, walletData =>
            {
                var existing = walletData.Get_output(keyId);

                if (existing != null)

                {
                    var keyId2 = existing.KeyId;
                    var derivation = existing.NChild;

                    return (Identifier.From_hex(keyId2), derivation);
                }
Beispiel #3
0
        /// Builds a transaction to send to someone from the HD seed associated with the
        /// walvar and the amount to send. Handles reading through the walvar data file,
        /// selecting outputs to spend and building the change.
        public static (Transaction tx, BlindingFactor blind, OutputData[] outputs, Identifier keyid) build_send_tx(
            WalletConfig config,
            Keychain keychain,
            ulong amount,
            ulong currentHeight,
            ulong minimumConfirmations,
            ulong lockHeight,
            uint maxOutputs,
            bool defaultStrategy
            )
        {
            var keyId = keychain.Root_key_id().Clone();

// select some spendable coins from the walvar
            var coins = WalletData.Read_wallet(config.DataFileDir,
                                               walletData => walletData.Select(
                                                   keyId.Clone(),
                                                   amount,
                                                   currentHeight,
                                                   minimumConfirmations,
                                                   maxOutputs,
                                                   defaultStrategy));

            // build transaction skevaron with inputs and change

            var(partsArray, changeKey) = inputs_and_change(coins, config, keychain, amount);

            var parts = partsArray.ToList();

            // This is more proof of concept than anything but here we set lock_height
            // on tx being sent (based on current chain height via api).
            parts.Add(c => c.with_lock_height(lockHeight));


            var(tx, blind) = Build.Transaction(parts.ToArray(), keychain);


            return(tx, blind, coins, changeKey);
        }
Beispiel #4
0
        /// Builds a single api query to retrieve the latest output data from the node.
        /// So we can refresh the local wallet outputs.
        public static bool refresh_outputs(WalletConfig config, Keychain keychain)

        {
            Log.Debug("Refreshing wallet outputs");


            var walletOutputs = new Dictionary <string, Identifier>();
            var commits       = new List <Commitment>();

            // build a local map of wallet outputs by commits
            // and a list of outputs we want to query the node for
            WalletData.Read_wallet(config.DataFileDir,
                                   walletData =>
            {
                foreach (var op in walletData
                         .Outputs
                         .Values
                         .Where(w => w.RootKeyId == keychain.Root_key_id().HexValue&& w.Status != OutputStatus.Spent))
                {
                    var commit = keychain.commit_with_key_index(op.Value, op.NChild);
                    commits.Add(commit);
                    walletOutputs.Add(commit.Hex, Identifier.From_hex(op.KeyId));
                }

                return(walletData);
            });

            // build the necessary query params -
            // ?id=xxx&id=yyy&id=zzz
            var queryParams = commits.Select(s => $"id={s.Hex}");

            var queryString = string.Join("&", queryParams);

            var url = $"{config.CheckNodeApiHttpAddr}/v1/chain/utxos/byids?{queryString}";


            // build a map of api outputs by commit so we can look them up efficiently
            var apiOutputs = new Dictionary <string, ApiOutput>();

            //HttpClient here
            // todo:asyncification



            HttpResponseMessage response;

            try
            {
                response = ApiClient.GetAsync(url).Result;
            }
            catch (Exception ex)
            {
                Log.Warning(ex, "Failed to refresh from {Url}", url);
                return(false);
            }


            // if we got anything other than 200 back from server, don't attempt to refresh the wallet
            //  data after
            if (!response.IsSuccessStatusCode)
            {
                Log.Warning("Failed to refresh from {Url} : {StatusCode} - {ReasonPhrase}", url, response.StatusCode, response.ReasonPhrase);
                return(false);
            }

            var content = response.Content.ReadAsStringAsync().Result;

            var outputs = JsonConvert.DeserializeObject <ApiOutput[]>(content);

            foreach (var op in outputs)
            {
                apiOutputs.Add(HexUtil.to_hex(op.Commit), op);
            }


// now for each commit, find the output in the wallet and
// the corresponding api output (if it exists)
// and refresh it in-place in the wallet.
// Note: minimizing the time we spend holding the wallet lock.
            WalletData.With_wallet(config.DataFileDir, walletData =>
            {
                foreach (var commit in commits)
                {
                    var id = walletOutputs[commit.Hex];

                    if (walletData.Outputs.TryGetValue(id.HexValue, out var op))
                    {
                        if (apiOutputs.TryGetValue(commit.Hex, out var apiOutput))
                        {
                            refresh_output(op, apiOutput);
                        }
                        else
                        {
                            mark_spent_output(op);
                        }
                    }
                }
                return(walletData);
            });

            return(true);
        }
Beispiel #5
0
        public static string build_info(WalletConfig config, Keychain keychain)
        {
            var sb = new StringBuilder();

            var result = Checker.refresh_outputs(config, keychain);


            WalletData.Read_wallet(config.DataFileDir, walletData =>
            {
                ulong currentHeight;

                try
                {
                    var tip       = Checker.get_tip_from_node(config);
                    currentHeight = tip.Height;
                }
                catch
                {
                    currentHeight = walletData.Outputs.Any() ? walletData.Outputs.Values.Max(m => m.Height) : 0;
                }


                ulong unspentTotal          = 0;
                ulong unspentButLockedTotal = 0;
                ulong unconfirmedTotal      = 0;
                ulong lockedTotal           = 0;


                foreach (var op in walletData.Outputs.Values.Where(w => w.RootKeyId == keychain.Root_key_id().HexValue))


                {
                    if (op.Status == OutputStatus.Unspent)
                    {
                        unspentTotal += op.Value;
                        if (op.LockHeight > currentHeight)
                        {
                            unspentButLockedTotal += op.Value;
                        }
                    }
                    if (op.Status == OutputStatus.Unconfirmed && !op.IsCoinbase)
                    {
                        unconfirmedTotal += op.Value;
                    }
                    if (op.Status == OutputStatus.Locked)
                    {
                        lockedTotal += op.Value;
                    }
                }

                var title = $"Wallet Summary Info - Block Height: {currentHeight}";

                sb.AppendLine($"{title}");
                sb.AppendLine("----------------------------------------------");
                sb.AppendLine("");

                sb.AppendLine($"Total:                          {unspentTotal + unconfirmedTotal}");
                sb.AppendLine($"Awaiting Confirmation:          {unconfirmedTotal}");
                sb.AppendLine($"Confirmed but Still Locked:     {unspentButLockedTotal}");
                sb.AppendLine($"Currently Spendable             {unspentTotal - unspentButLockedTotal}");
                sb.AppendLine("");
                sb.AppendLine("-----------------------------------------------");
                sb.AppendLine("");
                sb.AppendLine($"Locked by previous transaction: {lockedTotal}");

                return(walletData);
            });

            if (!result)
            {
                sb.AppendLine("");
                sb.AppendLine(
                    "WARNING - Showing local data only - Wallet was unable to contact a node to update and verify the info shown here.");
            }


            return(sb.ToString());
        }