Exemple #1
0
 public WalletAddr(WalletTag tag, string path, int pathIndex, string address)
 {
     this.Tag       = tag;
     this.Path      = path;
     this.PathIndex = pathIndex;
     this.Address   = address;
 }
Exemple #2
0
        public WalletTag NewTag(string tag)
        {
            var tag_ = new WalletTag {
                Tag = tag
            };

            db.WalletTags.Add(tag_);
            return(tag_);
        }
Exemple #3
0
        public WalletTag TagCreate(string tag)
        {
            var _tag = new WalletTag {
                Tag = tag
            };

            WalletTags.Add(_tag);
            return(_tag);
        }
Exemple #4
0
        public BitcoinAddress AddChangeAddress(WalletTag tag)
        {
            // get new unused address
            var keypathInfo = GetClient().GetUnused(pubkey, DerivationFeature.Change, reserve: false);
            var addr        = keypathInfo.ScriptPubKey.GetDestinationAddress(GetNetwork());
            // check that address is not already present in the db (we used 'reserve: false')
            var address = db.AddrGet(addr.ToString());

            if (address != null)
            {
                return(addr);
            }
            // add to address list
            address = new WalletAddr(tag, keypathInfo.KeyPath.ToString(), 0, addr.ToString());
            db.WalletAddrs.Add(address);
            return(addr);
        }
Exemple #5
0
        WalletTx AddOutgoingTx(string from, TransferTransaction signedTx, WalletTag tagFor)
        {
            var date    = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var address = db.AddrGet(from);
            var amount  = asset.AmountToLong(signedTx.Amount);
            var fee     = asset.AmountToLong(signedTx.Fee);

            logger.LogDebug("outgoing tx: amount: {0}, fee: {1}", amount, fee);
            // create chain tx
            var ctx = new ChainTx(signedTx.GenerateId(), date, fee, -1, 0);

            ctx.NetworkStatus = new ChainTxNetworkStatus(ctx, ChainTxStatus.Unconfirmed, date, signedTx.GetBytes());
            db.ChainTxs.Add(ctx);
            db.ChainTxNetworkStatus.Add(ctx.NetworkStatus);
            // create tx inputs
            var i = new TxInput(signedTx.GenerateId(), from /*signedTx.Sender is null*/, 0, amount);

            i.ChainTx    = ctx;
            i.WalletAddr = address;
            db.TxInputs.Add(i);
            i            = new TxInput(signedTx.GenerateId(), from /*signedTx.Sender is null*/, 1, fee);
            i.ChainTx    = ctx;
            i.WalletAddr = address;
            db.TxInputs.Add(i);
            // create tx output
            var o = new TxOutput(signedTx.GenerateId(), signedTx.Recipient, 0, amount);

            o.ChainTx = ctx;
            db.TxOutputs.Add(o);
            if (tagFor != null)
            {
                db.TxOutputsForTag.Add(new TxOutputForTag {
                    TxOutput = o, Tag = tagFor
                });
            }
            // create wallet tx
            var wtx = new WalletTx {
                ChainTx = ctx, Address = address, Direction = WalletDirection.Outgoing
            };

            db.WalletTxs.Add(wtx);
            return(wtx);
        }
Exemple #6
0
        WalletTx AddOutgoingTx(string from, string signedTx, WalletTag tagFor)
        {
            var address = db.AddrGet(from);
            var tx      = new Transaction(signedTx.HexToByteArray());
            var date    = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var fee     = HexBigIntegerConvertorExtensions.HexToBigInteger(tx.GasLimit.ToHex(), false) * HexBigIntegerConvertorExtensions.HexToBigInteger(tx.GasPrice.ToHex(), false);
            var amount  = HexBigIntegerConvertorExtensions.HexToBigInteger(tx.Value.ToHex(), false);

            logger.LogDebug("outgoing tx: amount: {0}, fee: {1}", amount, fee);
            // create chain tx
            var ctx = new ChainTx(tx.Hash.ToHex(true), date, fee, -1, 0);

            db.ChainTxs.Add(ctx);
            var networkStatus = new ChainTxNetworkStatus(ctx, ChainTxStatus.Unconfirmed, date, signedTx.HexToByteArray());

            db.ChainTxNetworkStatus.Add(networkStatus);
            // create tx input
            var i = new TxInput(tx.Hash.ToHex(true), from, 0, amount + fee);

            i.ChainTx    = ctx;
            i.WalletAddr = address;
            db.TxInputs.Add(i);
            // create tx output
            var o = new TxOutput(tx.Hash.ToHex(true), tx.ReceiveAddress.ToHex(true), 0, amount);

            o.ChainTx = ctx;
            db.TxOutputs.Add(o);
            if (tagFor != null)
            {
                db.TxOutputsForTag.Add(new TxOutputForTag {
                    TxOutput = o, Tag = tagFor
                });
            }
            // create wallet tx
            var wtx = new WalletTx {
                ChainTx = ctx, Address = address, Direction = WalletDirection.Outgoing
            };

            db.WalletTxs.Add(wtx);
            return(wtx);
        }
Exemple #7
0
        public override WalletError Spend(string tag, string tagChange, string to, BigInteger amount, BigInteger feeMax, BigInteger feeUnit, out IEnumerable <WalletTx> wtxs, WalletTag tagFor = null, string replaceTxId = null)
        {
            wtxs = new List <WalletTx>();
            var tagChange_ = db.TagGet(tagChange);

            Util.WalletAssert(tagChange_ != null, $"Tag '{tagChange}' does not exist");
            // create tx template with destination as first output
            var tx     = Transaction.Create(GetNetwork());
            var money  = new Money((ulong)amount);
            var toaddr = BitcoinAddress.Create(to, GetNetwork());
            var output = tx.Outputs.Add(money, toaddr);
            // create list of candidate coins to spend based on (a tx we might want to replace and) UTXOs from the selected tag
            var addrs      = GetAddresses(tag);
            var candidates = new List <CoinCandidate>();
            var res        = UseReplacedTxCoins(addrs, candidates, replaceTxId);

            if (res != WalletError.Success)
            {
                return(res);
            }
            var utxos = GetClient().GetUTXOs(pubkey);

            UseUtxoCoins(addrs, candidates, utxos);
            // add inputs until we can satisfy our output
            BigInteger totalInput = 0;
            var        toBeSpent  = new List <CoinSpend>();

            foreach (var candidate in candidates)
            {
                // add to list of coins and private keys to spend
                var txin = new TxIn(candidate.Coin.Outpoint);
                txin.Sequence = 0; // RBF: BIP125
                tx.Inputs.Add(txin);
                totalInput += candidate.Coin.Amount.Satoshi;
                var privateKey = key.ExtKey.Derive(new KeyPath(candidate.Addr.Path)).PrivateKey;
                toBeSpent.Add(new CoinSpend(candidate.Addr.Address, candidate.Addr, candidate.Coin, privateKey));
                // check if we have enough inputs
                if (totalInput >= amount)
                {
                    // check if we have enough fee
                    if (GetFeeRate(tx, toBeSpent).SatoshiPerByte > (decimal)feeUnit)
                    {
                        break;
                    }
                }
            }
            // check we have enough inputs
            logger.LogDebug($"totalInput {totalInput}, amount: {amount}");
            if (totalInput < amount)
            {
                logger.LogError("insufficient funds: total inputs are less then sending amount");
                return(WalletError.InsufficientFunds);
            }
            // check fee rate
            var feeRate = GetFeeRate(tx, toBeSpent);

            if (feeRate.SatoshiPerByte > (decimal)feeUnit)
            {
                // create a change address
                var changeAddress = AddChangeAddress(tagChange_);
                // calculate the target fee
                var currentFee   = feeRate.GetFee(tx.GetVirtualSize());
                var targetFee    = tx.GetVirtualSize() * (long)feeUnit;
                var changeOutput = new TxOut(currentFee - targetFee, changeAddress);
                targetFee += output.GetSerializedSize() * (long)feeUnit;
                // add the change output
                changeOutput = tx.Outputs.Add(currentFee - targetFee, changeAddress);
            }
            else if (feeRate.SatoshiPerByte < (decimal)feeUnit)
            {
                logger.LogError($"insufficient funds: fee rate ({feeRate.SatoshiPerByte} sats/byte) is less then {feeUnit}");
                return(WalletError.InsufficientFunds);
            }
            var coins = from a in toBeSpent select a.Coin;
            var keys  = from a in toBeSpent select a.Key;

            // check coins are represented as incoming wallet txs
            CheckCoinsAreInWallet(candidates, utxos.CurrentHeight);
            // sign inputs (after adding a change output)
            tx.Sign(keys.ToArray(), coins.ToArray());
            // recalculate fee rate and check it is less then the max fee
            var fee = tx.GetFee(coins.ToArray());

            if (fee.Satoshi > feeMax)
            {
                return(WalletError.MaxFeeBreached);
            }
            // broadcast transaction
            var result = GetClient().Broadcast(tx);

            if (result.Success)
            {
                // log outgoing transaction
                var coinOutput = new CoinOutput(to, amount);
                var wtxs_      = AddOutgoingTx(tx, toBeSpent, new List <CoinOutput> {
                    coinOutput
                }, fee.Satoshi, tagFor);
                ((List <WalletTx>)wtxs).AddRange(wtxs_);
                return(WalletError.Success);
            }
            else
            {
                logger.LogError("{0}, {1}, {2}", result.RPCCode, result.RPCCodeMessage, result.RPCMessage);
                return(WalletError.FailedBroadcast);
            }
        }
Exemple #8
0
        IEnumerable <WalletTx> AddOutgoingTx(Transaction tx, List <CoinSpend> spents, List <CoinOutput> outputs, BigInteger fee, WalletTag tagFor)
        {
            var txid = tx.GetHash().ToString();

            logger.LogDebug("outgoing tx: outputs: {0}, fee: {1}", outputs.Select(o => o.Amount), fee);
            var date = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            // create chain tx
            var ctx = db.ChainTxGet(txid);

            if (ctx == null)
            {
                ctx = new ChainTx(txid, date, fee, -1, 0);
                db.ChainTxs.Add(ctx);
            }
            if (ctx.NetworkStatus == null)
            {
                var networkStatus = new ChainTxNetworkStatus(ctx, ChainTxStatus.Unconfirmed, date, tx.ToBytes());
                db.ChainTxNetworkStatus.Add(networkStatus);
            }
            // create tx inputs
            uint n = 0;

            foreach (var spent in spents)
            {
                if (db.TxInputGet(txid, n) == null)
                {
                    var i = new TxInput(txid, spent.From, n, spent.Coin.Amount.Satoshi);
                    i.ChainTx    = ctx;
                    i.WalletAddr = spent.Addr;
                    db.TxInputs.Add(i);
                }
                n++;
            }
            // create tx outputs
            n = 0;
            foreach (var output in outputs)
            {
                if (db.TxOutputGet(txid, n) == null)
                {
                    var o = new TxOutput(txid, output.To, n, output.Amount);
                    o.ChainTx = ctx;
                    db.TxOutputs.Add(o);
                    if (tagFor != null)
                    {
                        db.TxOutputsForTag.Add(new TxOutputForTag {
                            TxOutput = o, Tag = tagFor
                        });
                    }
                }
                n++;
            }
            // create wallet txs
            var wtxs = new List <WalletTx>();

            foreach (var spent in spents)
            {
                if (spent.Addr == null)
                {
                    continue;
                }
                var wtx = db.TxGet(spent.Addr, ctx, WalletDirection.Outgoing);
                if (wtx == null)
                {
                    wtx = new WalletTx {
                        ChainTx = ctx, Address = spent.Addr, Direction = WalletDirection.Outgoing, State = WalletTxState.None
                    };
                    db.WalletTxs.Add(wtx);
                }
                wtxs.Add(wtx);
            }
            return(wtxs);
        }
Exemple #9
0
        public WalletPendingSpend RegisterPendingSpend(string tag, string tagChange, string to, BigInteger amount, WalletTag tagFor = null)
        {
            if (!ValidateAddress(to))
            {
                return(null);
            }
            var spendCode = Utils.CreateDepositCode(allowLetters: true);

            while (db.PendingSpendGet(spendCode) != null)
            {
                spendCode = Utils.CreateDepositCode(allowLetters: true);
            }
            var tag_ = db.TagGet(tag);

            Util.WalletAssert(tag_ != null, $"Tag '{tag}' does not exist");
            var tagChange_ = db.TagGet(tagChange);

            Util.WalletAssert(tagChange_ != null, $"Tag '{tagChange}' does not exist");
            var date  = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var spend = new WalletPendingSpend {
                SpendCode = spendCode, Date = date, State = PendingSpendState.Pending, Tag = tag_, TagChange = tagChange_, To = to, Amount = amount
            };

            spend.TagFor = tagFor;
            db.WalletPendingSpends.Add(spend);
            if (tagFor != null)
            {
                db.PendingSpendsForTag.Add(new PendingSpendForTag {
                    PendingSpend = spend, Tag = tagFor
                });
            }
            return(spend);
        }
Exemple #10
0
 public abstract WalletError Spend(string tag, string tagChange, string to, BigInteger amount, BigInteger feeMax, BigInteger feeUnit, out IEnumerable <WalletTx> wtxs, WalletTag tagOnBehalfOf = null, string ReplaceTxId = null);
Exemple #11
0
        public override WalletError Spend(string tag, string tagChange, string to, BigInteger amount, BigInteger feeMax, BigInteger feeUnit, out IEnumerable <WalletTx> wtxs, WalletTag tagFor = null,
                                          string replaceTxId = null /*not applicable to eth*/)
        {
            wtxs = new List <WalletTx>();
            // get gas price
            //var gasPriceTask = web3.Eth.GasPrice.SendRequestAsync();
            //gasPriceTask.Wait();
            //var gasPrice = gasPriceTask.Result.Value;
            var gasPrice = feeUnit;
            // create spend transaction from accounts
            var accts = GetAddresses(tag);
            Tuple <string, string> signedSpendTx;
            //TODO: check gas price, and/or allow custom setting (max total wei too?)
            var res = CreateSpendTx(accts, to, amount, gasPrice, TX_GAS, feeMax, out signedSpendTx);

            if (res == WalletError.Success)
            {
                // send the raw signed transaction and get the txid
                try
                {
                    var sendTxTask = web3.Eth.Transactions.SendRawTransaction.SendRequestAsync(signedSpendTx.Item2);
                    sendTxTask.Wait();
                    var txid = sendTxTask.Result;
                    // add to wallet data
                    var wtx = AddOutgoingTx(signedSpendTx.Item1, signedSpendTx.Item2, tagFor);
                    ((List <WalletTx>)wtxs).Add(wtx);
                }
                catch (Exception ex)
                {
                    logger.LogError("{0}", ex);
                    return(WalletError.FailedBroadcast);
                }
            }
            return(res);
        }
Exemple #12
0
        public override WalletError Spend(string tag, string tagChange, string to, BigInteger amount, BigInteger feeMax, BigInteger feeUnit, out IEnumerable <WalletTx> wtxs, WalletTag tagFor = null,
                                          string replaceTxId = null /*not applicable to waves*/)
        {
            wtxs = new List <WalletTx>();
            // create spend transaction from accounts
            var accts = GetAddresses(tag);
            Tuple <string, TransferTransaction> signedSpendTx;
            var res = CreateSpendTx(accts, to, amount, feeUnit, feeMax, out signedSpendTx);

            if (res == WalletError.Success)
            {
                // send the raw signed transaction and get the txid
                try
                {
                    var output = node.Broadcast(signedSpendTx.Item2);
                    logger.LogDebug(output);
                    var txid = signedSpendTx.Item2.GenerateId();
                }
                catch (System.Net.WebException ex)
                {
                    logger.LogError(ex, "error broadcasting tx");
                    var     resp = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
                    dynamic obj  = JsonConvert.DeserializeObject(resp);
                    if (obj != null)
                    {
                        logger.LogError($"message: {obj.message}");
                    }
                    return(WalletError.PartialBroadcast);
                }
                // add to wallet data
                var wtx = AddOutgoingTx(signedSpendTx.Item1, signedSpendTx.Item2, tagFor);
                ((List <WalletTx>)wtxs).Add(wtx);
            }
            return(res);
        }