public static ISegWitAddress Match(this ISegWitAddress segWitAddress, string address = null, AddressType addressType = AddressType.MatchAll) { if (segWitAddress == null) // no op { return(null); } if (address != null) // filter by address { if (segWitAddress.Address == address) { if (addressType == AddressType.MatchAll || addressType == segWitAddress.AddressType) { return(segWitAddress); } return(null); } return(null); } // do not filter by address if (addressType == AddressType.MatchAll) { return(segWitAddress); } if (addressType == segWitAddress.AddressType) { return(segWitAddress); } return(null); }
public SegWitCoin(ISegWitAddress segWitAddress, uint256 utxoTxHash, int utxoTxN, long utxoValue) { this.SegWitAddress = segWitAddress; this.UtxoTxHash = utxoTxHash; this.UtxoTxN = utxoTxN; this.UtxoValue = utxoValue; }
public static ISegWitAddress GetOrAddAddress(string bech32, int blockHeight, X1WalletFile x1WalletFile) { ISegWitAddress existing = null; if (x1WalletFile.PubKeyHashAddresses.TryGetValue(bech32, out var pubKeyHashAddress)) { existing = pubKeyHashAddress; } else if (x1WalletFile.ColdStakingAddresses.TryGetValue(bech32, out var coldStakingAddress)) { existing = coldStakingAddress; } else if (x1WalletFile.MultiSigAddresses.TryGetValue(bech32, out var multiSigAddress)) { existing = multiSigAddress; } if (existing != null) { existing.LastSeenHeight = blockHeight; x1WalletFile.SaveX1WalletFile(x1WalletFile.CurrentPath); return(existing); } if (x1WalletFile.LookAhead.TryGetValue(bech32, out var gapAddress)) { gapAddress.LastSeenHeight = blockHeight; x1WalletFile.PubKeyHashAddresses[gapAddress.Address] = gapAddress; x1WalletFile.LookAhead.TryRemove(gapAddress.Address, out var _); x1WalletFile.SaveX1WalletFile(x1WalletFile.CurrentPath); return(gapAddress); } return(null); }
static Dictionary <string, UtxoMetadata> ExtractIncomingFunds(Transaction transaction, int blockHeight, bool didSpend, Func <string, int, ISegWitAddress> addressReader, out long amountReceived, out Dictionary <string, UtxoMetadata> destinations) { Dictionary <string, UtxoMetadata> received = null; Dictionary <string, UtxoMetadata> notInWallet = null; long sum = 0; int index = 0; foreach (var output in transaction.Outputs) { ISegWitAddress ownAddress = null; if (!output.IsProtocolOutput(transaction)) { ownAddress = addressReader(output.ScriptPubKey.GetAddressFromScriptPubKey(), blockHeight); } if (ownAddress != null) { NotNull(ref received, transaction.Outputs.Count); var item = new UtxoMetadata { Address = ownAddress.Address, HashTx = transaction.GetHash(), Satoshis = output.Value.Satoshi, Index = index }; received.Add(item.GetKey(), item); sum += item.Satoshis; } else { // For protocol tx, we are not interested in the other outputs. // If we spent, the save the destinations, because the wallet wants to show where we sent coins to. // if we did not spent, we do not save the destinations, because they are the other parties change address // and we only received coins. if (!transaction.IsCoinBase && !transaction.IsCoinStake && didSpend) { NotNull(ref notInWallet, transaction.Outputs.Count); var dest = new UtxoMetadata { Address = output.ScriptPubKey.GetAddressFromScriptPubKey(), HashTx = transaction.GetHash(), Satoshis = output.Value != null ? output.Value.Satoshi : 0, Index = index }; notInWallet.Add(dest.GetKey(), dest); } } index++; } destinations = received != null ? notInWallet : null; amountReceived = sum; return(received); }
static bool IsAddressUsedInConfirmedTransactions(ISegWitAddress address, IReadOnlyCollection <BlockMetadata> blocks) { // slow version foreach (BlockMetadata block in blocks) { foreach (TransactionMetadata tx in block.Transactions) { foreach (var utxo in tx.Received.Values) { if (utxo.Address == address.Address) { return(true); } } } } return(false); }
public SegWitCoin(ISegWitAddress segWitAddress, uint256 utxoTxHash, int utxoTxN, long utxoValue, UtxoType utxoType, uint?utxoPosV3Time) : this(segWitAddress, utxoTxHash, utxoTxN, utxoValue, utxoType) { this.UtxoPosV3Time = utxoPosV3Time; }
public static Script GetScriptPubKey(this ISegWitAddress address) { return(GetScriptPubKey(address.Address)); }
public static Balance GetBalance(ConcurrentDictionary <int, BlockMetadata> blocks, int syncedHeight, HashSet <MemoryPoolEntry> memoryPoolEntries, Func <string, ISegWitAddress> getOwnAddress, string matchAddress = null, AddressType matchAddressType = AddressType.MatchAll) { var balance = new Balance(); var spent = new Dictionary <string, UtxoMetadata>(); // process all confirmed transactions first, oldest to newest foreach (var(height, block) in blocks) { foreach (var tx in block.Transactions) { bool isImmatureForSpending = false; if (tx.TxType.HasCoinbaseMaturity()) { var confirmationsSpending = syncedHeight - height + 1; // if the tip is at 100 and my tx is height 90, it's 11 confirmations isImmatureForSpending = confirmationsSpending < C.Network.Consensus.CoinbaseMaturity; // ok } var confirmationsStaking = syncedHeight - height + 1; // if the tip is at 100 and my tx is height 90, it's 11 confirmations var isImmatureForStaking = confirmationsStaking < C.Network.Consensus.MaxReorgLength; if (tx.Received != null) { foreach (UtxoMetadata utxo in tx.Received.Values) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalReceived += utxo.Satoshis; var coin = new SegWitCoin(address, utxo.HashTx, utxo.Index, utxo.Satoshis); if (!isImmatureForSpending) { balance.Spendable += utxo.Satoshis; balance.SpendableCoins.AddSafe(utxo.GetKey(), coin); } if (!isImmatureForStaking) { balance.Stakable += utxo.Satoshis; balance.StakingCoins.AddSafe(utxo.GetKey(), coin); } } } } if (tx.Spent != null) { foreach ((string txIdN, UtxoMetadata utxo) in tx.Spent) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalSpent += utxo.Satoshis; spent.AddSafe(txIdN, utxo); } } } } } // unconfirmed transactions - add them last, ordered by time, so that they come last in coin selection // when unconfirmed outputs get spent, to allow the memory pool and network to recognize // the new unspent outputs. foreach (MemoryPoolEntry entry in memoryPoolEntries.OrderBy(x => x.TransactionTime)) { var tx = entry.Transaction; if (tx.Received != null) { foreach (UtxoMetadata utxo in tx.Received.Values) { ISegWitAddress address = getOwnAddress(utxo.Address); balance.TotalReceivedPending += utxo.Satoshis; var coin = new SegWitCoin(address, utxo.HashTx, utxo.Index, utxo.Satoshis); balance.SpendableCoins.AddSafe(utxo.GetKey(), coin); } } if (tx.Spent != null) { foreach (var(txIdN, utxo) in tx.Spent) { for (var address = getOwnAddress(utxo.Address).Match(matchAddress, matchAddressType); address != null; address = null) { balance.TotalSpent += utxo.Satoshis; spent.AddSafe(txIdN, utxo); } } } } // remove what is already spent foreach (var utxoId in spent) { if (balance.SpendableCoins.ContainsKey(utxoId.Key)) { balance.Spendable -= utxoId.Value.Satoshis; balance.SpendableCoins.Remove(utxoId.Key); } if (balance.StakingCoins.ContainsKey(utxoId.Key)) { balance.Stakable -= utxoId.Value.Satoshis; balance.StakingCoins.Remove(utxoId.Key); } } // last balance updates balance.Confirmed = balance.TotalReceived - balance.TotalSpent; balance.Pending = balance.TotalReceivedPending - balance.TotalSpentPending; balance.Total = balance.Confirmed + balance.Pending; return(balance); }