public WalletAddr(WalletTag tag, string path, int pathIndex, string address) { this.Tag = tag; this.Path = path; this.PathIndex = pathIndex; this.Address = address; }
public WalletTag NewTag(string tag) { var tag_ = new WalletTag { Tag = tag }; db.WalletTags.Add(tag_); return(tag_); }
public WalletTag TagCreate(string tag) { var _tag = new WalletTag { Tag = tag }; WalletTags.Add(_tag); return(_tag); }
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); }
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); }
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); }
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); } }
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); }
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); }
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);
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); }
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); }