Beispiel #1
0
 public object ToJson()
 {
     return(new
     {
         number = Number?.ToJson(),
         hash = Hash?.ToJson(),
         parentHash = ParentHash?.ToJson(),
         nonce = Nonce?.ToJson(),
         MixHash = MixHash?.ToJson(),
         sha3Uncles = Sha3Uncles?.ToJson(),
         logsBloom = LogsBloom?.ToJson(),
         transactionsRoot = TransactionsRoot?.ToJson(),
         stateRoot = StateRoot?.ToJson(),
         receiptsRoot = ReceiptsRoot?.ToJson(),
         miner = Miner?.ToJson(),
         difficulty = Difficulty?.ToJson(),
         totalDifficulty = TotalDifficulty?.ToJson(),
         extraData = ExtraData?.ToJson(),
         size = Size?.ToJson(),
         gasLimit = GasLimit?.ToJson(),
         gasUsed = GasUsed?.ToJson(),
         timestamp = Timestamp?.ToJson(),
         transactions = Transactions?.Select(x => x.ToJson()).ToArray() ?? TransactionHashes?.Select(x => x.ToJson()).ToArray(),
         uncles = Uncles?.Select(x => x.ToJson()).ToArray()
     });
 }
Beispiel #2
0
 public Block CreateBlock()
 {
     if (Block is null)
     {
         Block = MakeHeader();
         if (Block == null)
         {
             return(null);
         }
         Contract contract            = Contract.CreateMultiSigContract(this.M(), Validators);
         ContractParametersContext sc = new ContractParametersContext(Block);
         for (int i = 0, j = 0; i < Validators.Length && j < this.M(); i++)
         {
             if (CommitPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber)
             {
                 continue;
             }
             sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage <Commit>().Signature);
             j++;
         }
         Block.Witness      = sc.GetWitnesses()[0];
         Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray();
     }
     return(Block);
 }
Beispiel #3
0
        /// <summary>
        /// Gets the transaction statuses.
        /// </summary>
        /// <param name="hashes">The hashes.</param>
        /// <returns>IObservable&lt;List&lt;TransactionStatusDTO&gt;&gt;.</returns>
        /// <exception cref="ArgumentNullException">hashes</exception>
        /// <exception cref="ArgumentException">Collection contains one or more invalid hashes.</exception>
        public IObservable <List <TransactionStatusDTO> > GetTransactionStatuses(TransactionHashes hashes)
        {
            if (hashes == null)
            {
                throw new ArgumentNullException(nameof(hashes));
            }
            if (hashes.hashes.Any(hash => hash.Length != 64 || !Regex.IsMatch(hash, @"\A\b[0-9a-fA-F]+\b\Z")))
            {
                throw new ArgumentException("Collection contains one or more invalid hashes.");
            }

            return(Observable.FromAsync(async ar => await TransactionRoutesApi.GetTransactionsStatusesAsync(hashes)));
        }
        /// <summary>
        /// Tries to perform mempool cleanup with the help of the backend.
        /// </summary>
        public async Task <bool> TryPerformMempoolCleanupAsync(Func <Uri> destAction, IPEndPoint torSocks)
        {
            // If already cleaning, then no need to run it that often.
            if (Interlocked.CompareExchange(ref _cleanupInProcess, 1, 0) == 1)
            {
                return(false);
            }

            // This function is designed to prevent forever growing mempool.
            try
            {
                if (!TransactionHashes.Any())
                {
                    return(true);                    // There's nothing to cleanup.
                }

                Logger.LogInfo <MempoolService>("Start cleaning out mempool...");
                using (var client = new WasabiClient(destAction, torSocks))
                {
                    var compactness      = 10;
                    var allMempoolHashes = await client.GetMempoolHashesAsync(compactness);

                    var toRemove = TransactionHashes.Where(x => !allMempoolHashes.Contains(x.ToString().Substring(0, compactness)));

                    int removedTxCount = 0;
                    foreach (uint256 tx in toRemove)
                    {
                        if (TransactionHashes.TryRemove(tx))
                        {
                            removedTxCount++;
                        }
                    }

                    Logger.LogInfo <MempoolService>($"{removedTxCount} transactions were cleaned from mempool.");
                }

                return(true);
            }
            catch (Exception ex)
            {
                Logger.LogWarning <MempoolService>(ex);
            }
            finally
            {
                Interlocked.Exchange(ref _cleanupInProcess, 0);
            }

            return(false);
        }
        /// <summary>
        ///     Get a list of transaction statuses for a given array of transaction hashes
        /// </summary>
        /// <param name="transactionHashes">Transaction hashes</param>
        /// <returns>List&lt;IObservable&lt;TransactionStatus&gt;&gt;</returns>
        public IObservable <List <TransactionStatus> > GetTransactionStatuses(List <string> transactionHashes)
        {
            if (transactionHashes.Count < 0)
            {
                throw new ArgumentNullException(nameof(transactionHashes));
            }

            var hashes = new TransactionHashes
            {
                Hashes = transactionHashes
            };

            var route = $"{BasePath}/transactionStatus/{transactionHashes}";

            return(Observable.FromAsync(async ar =>
                                        await route.PostJsonAsync(transactionHashes).ReceiveJson <List <TransactionStatusDTO> >())
                   .Select(h => h.Select(hash => new TransactionStatus(hash.Group, hash.Status, hash.Hash,
                                                                       hash.Deadline.ToUInt64(), hash.Height.ToUInt64())).ToList()));
        }
Beispiel #6
0
        public Block Add <T>(string hash) where T : ISignable
        {
            IsFinalized = false;

            if (typeof(T) == typeof(Smartcontract))
            {
                SmartcontractHashes.Add(hash);
            }
            else if (typeof(T) == typeof(Transaction))
            {
                TransactionHashes.Add(hash);
            }
            else
            {
                throw new ArgumentException("Wrong type this should never happen!");
            }

            return(this);
        }
Beispiel #7
0
        /// <summary>
        /// Generates the hash of the block
        /// </summary>
        /// <param name="block"></param>
        /// <returns></returns>
        public IDownloadable GenerateHash()
        {
            Curl curl = new Curl();

            curl.Absorb(TryteString.FromAsciiString(Height + "").ToTrits());
            curl.Absorb(TryteString.FromAsciiString(Time + "").ToTrits());
            curl.Absorb(TryteString.FromAsciiString(Owner).ToTrits());
            curl.Absorb(TryteString.FromAsciiString(SendTo).ToTrits());
            curl.Absorb(TryteString.FromAsciiString(CoinName).ToTrits());

            curl.Absorb(TryteString.FromAsciiString(TransactionHashes.HashList(20) + "").ToTrits());
            curl.Absorb(TryteString.FromAsciiString(SmartcontractHashes.HashList(20) + "").ToTrits());

            var hash = new int[243];

            curl.Squeeze(hash);

            Hash = Converter.TritsToTrytes(hash);

            return(this);
        }
        public Block CreateBlock()
        {
            Block block = MakeHeader();

            if (block == null)
            {
                return(null);
            }
            Contract contract            = Contract.CreateMultiSigContract(M, Validators);
            ContractParametersContext sc = new ContractParametersContext(block);

            for (int i = 0, j = 0; i < Validators.Length && j < M; i++)
            {
                if (Signatures[i] != null)
                {
                    sc.AddSignature(contract, Validators[i], Signatures[i]);
                    j++;
                }
            }
            sc.Verifiable.Witnesses = sc.GetWitnesses();
            block.Transactions      = TransactionHashes.Select(p => Transactions[p]).ToArray();
            return(block);
        }
        /// <summary>
        /// Tries to perform mempool cleanup with the help of the connected nodes.
        /// NOTE: This results in heavy network activity! https://github.com/zkSNACKs/WalletWasabi/issues/1273
        /// </summary>
        public async Task <bool> TryPerformMempoolCleanupAsync(NodesGroup nodes, CancellationToken cancel)
        {
            // If already cleaning, then no need to run it that often.
            if (Interlocked.CompareExchange(ref _cleanupInProcess, 1, 0) == 1)
            {
                return(false);
            }

            // This function is designed to prevent forever growing mempool.
            try
            {
                if (!TransactionHashes.Any())
                {
                    return(true);                                          // There's nothing to cleanup.
                }
                var delay = TimeSpan.FromMinutes(1);
                if (nodes?.ConnectedNodes is null)
                {
                    return(false);
                }
                while (nodes.ConnectedNodes.Count != nodes.MaximumNodeConnection && nodes.ConnectedNodes.All(x => x.IsConnected))
                {
                    if (cancel.IsCancellationRequested)
                    {
                        return(false);
                    }
                    Logger.LogInfo <MemPoolService>($"Not all nodes were in a connected state. Delaying mempool cleanup for {delay.TotalSeconds} seconds...");
                    await Task.Delay(delay, cancel);
                }

                Logger.LogInfo <MemPoolService>("Start cleaning out mempool...");

                var allTxs = new HashSet <uint256>();
                foreach (Node node in nodes.ConnectedNodes)
                {
                    try
                    {
                        if (!node.IsConnected)
                        {
                            continue;
                        }

                        if (cancel.IsCancellationRequested)
                        {
                            return(false);
                        }
                        uint256[] txs = node.GetMempool(cancel);
                        if (cancel.IsCancellationRequested)
                        {
                            return(false);
                        }
                        allTxs.UnionWith(txs);
                        if (cancel.IsCancellationRequested)
                        {
                            return(false);
                        }
                    }
                    catch (Exception ex) when((ex is InvalidOperationException && ex.Message.StartsWith("The node is not in a connected state", StringComparison.InvariantCultureIgnoreCase)) ||
                                              ex is OperationCanceledException ||
                                              ex is TaskCanceledException ||
                                              ex is TimeoutException)
                    {
                        Logger.LogTrace <MemPoolService>(ex);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogDebug <MemPoolService>(ex);
                    }
                }

                uint256[] toRemove = TransactionHashes.Except(allTxs).ToArray();
                foreach (uint256 tx in toRemove)
                {
                    TransactionHashes.TryRemove(tx);
                }
                Logger.LogInfo <MemPoolService>($"{toRemove.Count()} transactions were cleaned from mempool.");

                return(true);
            }
            catch (Exception ex) when(ex is OperationCanceledException ||
                                      ex is TaskCanceledException ||
                                      ex is TimeoutException)
            {
                Logger.LogTrace <MemPoolService>(ex);
            }
            catch (Exception ex)
            {
                Logger.LogDebug <MemPoolService>(ex);
            }
            finally
            {
                Interlocked.Exchange(ref _cleanupInProcess, 0);
            }

            return(false);
        }
Beispiel #10
0
 public byte[] ContainsTransaction(byte[] txHash)
 {
     return(TransactionHashes.FirstOrDefault(t => t.BytesEqual(txHash)));
 }
Beispiel #11
0
        public bool Process(SmartTransaction tx)
        {
            uint256 txId           = tx.GetHash();
            var     walletRelevant = false;

            bool justUpdate = false;

            if (tx.Confirmed)
            {
                TransactionHashes.TryRemove(txId);                 // If we have in mempool, remove.
                if (!tx.Transaction.PossiblyP2WPKHInvolved())
                {
                    return(false);                    // We do not care about non-witness transactions for other than mempool cleanup.
                }

                bool isFoundTx = TransactionCache.Contains(tx);                 // If we have in cache, update height.
                if (isFoundTx)
                {
                    SmartTransaction foundTx = TransactionCache.FirstOrDefault(x => x == tx);
                    if (foundTx != default(SmartTransaction))                     // Must check again, because it's a concurrent collection!
                    {
                        foundTx.SetHeight(tx.Height, tx.BlockHash, tx.BlockIndex);
                        walletRelevant = true;
                        justUpdate     = true;                     // No need to check for double spend, we already processed this transaction, just update it.
                    }
                }
            }
            else if (!tx.Transaction.PossiblyP2WPKHInvolved())
            {
                return(false);                // We do not care about non-witness transactions for other than mempool cleanup.
            }

            if (!justUpdate && !tx.Transaction.IsCoinBase)             // Transactions we already have and processed would be "double spends" but they shouldn't.
            {
                var doubleSpends = new List <SmartCoin>();
                foreach (SmartCoin coin in Coins)
                {
                    var spent = false;
                    foreach (TxoRef spentOutput in coin.SpentOutputs)
                    {
                        foreach (TxIn txIn in tx.Transaction.Inputs)
                        {
                            if (spentOutput.TransactionId == txIn.PrevOut.Hash && spentOutput.Index == txIn.PrevOut.N)                             // Do not do (spentOutput == txIn.PrevOut), it's faster this way, because it won't check for null.
                            {
                                doubleSpends.Add(coin);
                                spent          = true;
                                walletRelevant = true;
                                break;
                            }
                        }
                        if (spent)
                        {
                            break;
                        }
                    }
                }

                if (doubleSpends.Any())
                {
                    if (tx.Height == Height.Mempool)
                    {
                        // if the received transaction is spending at least one input already
                        // spent by a previous unconfirmed transaction signaling RBF then it is not a double
                        // spanding transaction but a replacement transaction.
                        if (doubleSpends.Any(x => x.IsReplaceable))
                        {
                            // remove double spent coins (if other coin spends it, remove that too and so on)
                            // will add later if they came to our keys
                            foreach (SmartCoin doubleSpentCoin in doubleSpends.Where(x => !x.Confirmed))
                            {
                                Coins.TryRemove(doubleSpentCoin);
                            }
                            tx.SetReplacement();
                            walletRelevant = true;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    else                     // new confirmation always enjoys priority
                    {
                        // remove double spent coins recursively (if other coin spends it, remove that too and so on), will add later if they came to our keys
                        foreach (SmartCoin doubleSpentCoin in doubleSpends)
                        {
                            Coins.TryRemove(doubleSpentCoin);
                        }
                        walletRelevant = true;
                    }
                }
            }

            var  isLikelyCoinJoinOutput = false;
            bool hasEqualOutputs        = tx.Transaction.GetIndistinguishableOutputs(includeSingle: false).FirstOrDefault() != default;

            if (hasEqualOutputs)
            {
                var  receiveKeys         = KeyManager.GetKeys(x => tx.Transaction.Outputs.Any(y => y.ScriptPubKey == x.P2wpkhScript));
                bool allReceivedInternal = receiveKeys.All(x => x.IsInternal);
                if (allReceivedInternal)
                {
                    // It is likely a coinjoin if the diff between receive and sent amount is small and have at least 2 equal outputs.
                    Money spentAmount    = Coins.Where(x => tx.Transaction.Inputs.Any(y => y.PrevOut.Hash == x.TransactionId && y.PrevOut.N == x.Index)).Sum(x => x.Amount);
                    Money receivedAmount = tx.Transaction.Outputs.Where(x => receiveKeys.Any(y => y.P2wpkhScript == x.ScriptPubKey)).Sum(x => x.Value);
                    bool  receivedAlmostAsMuchAsSpent = spentAmount.Almost(receivedAmount, Money.Coins(0.005m));

                    if (receivedAlmostAsMuchAsSpent)
                    {
                        isLikelyCoinJoinOutput = true;
                    }
                }
            }

            List <SmartCoin> spentOwnCoins = null;

            for (var i = 0U; i < tx.Transaction.Outputs.Count; i++)
            {
                // If transaction received to any of the wallet keys:
                var      output   = tx.Transaction.Outputs[i];
                HdPubKey foundKey = KeyManager.GetKeyForScriptPubKey(output.ScriptPubKey);
                if (foundKey != default)
                {
                    walletRelevant = true;

                    if (output.Value <= DustThreshold)
                    {
                        continue;
                    }

                    foundKey.SetKeyState(KeyState.Used, KeyManager);
                    spentOwnCoins = spentOwnCoins ?? Coins.Where(x => tx.Transaction.Inputs.Any(y => y.PrevOut.Hash == x.TransactionId && y.PrevOut.N == x.Index)).ToList();
                    var anonset = tx.Transaction.GetAnonymitySet(i);
                    if (spentOwnCoins.Count != 0)
                    {
                        anonset += spentOwnCoins.Min(x => x.AnonymitySet) - 1;                         // Minus 1, because do not count own.
                    }

                    SmartCoin newCoin = new SmartCoin(txId, i, output.ScriptPubKey, output.Value, tx.Transaction.Inputs.ToTxoRefs().ToArray(), tx.Height, tx.IsRBF, anonset, isLikelyCoinJoinOutput, foundKey.Label, spenderTransactionId: null, false, pubKey: foundKey);                     // Do not inherit locked status from key, that's different.
                    // If we did not have it.
                    if (Coins.TryAdd(newCoin))
                    {
                        TransactionCache.TryAdd(tx);
                        CoinReceived?.Invoke(this, newCoin);

                        // Make sure there's always 21 clean keys generated and indexed.
                        KeyManager.AssertCleanKeysIndexed(isInternal: foundKey.IsInternal);

                        if (foundKey.IsInternal)
                        {
                            // Make sure there's always 14 internal locked keys generated and indexed.
                            KeyManager.AssertLockedInternalKeysIndexed(14);
                        }
                    }
                    else                                      // If we had this coin already.
                    {
                        if (newCoin.Height != Height.Mempool) // Update the height of this old coin we already had.
                        {
                            SmartCoin oldCoin = Coins.FirstOrDefault(x => x.TransactionId == txId && x.Index == i);
                            if (oldCoin != null)                             // Just to be sure, it is a concurrent collection.
                            {
                                oldCoin.Height = newCoin.Height;
                            }
                        }
                    }
                }
            }

            // If spends any of our coin
            for (var i = 0; i < tx.Transaction.Inputs.Count; i++)
            {
                var input = tx.Transaction.Inputs[i];

                var foundCoin = Coins.FirstOrDefault(x => x.TransactionId == input.PrevOut.Hash && x.Index == input.PrevOut.N);
                if (foundCoin != null)
                {
                    walletRelevant = true;
                    var alreadyKnown = foundCoin.SpenderTransactionId == txId;
                    foundCoin.SpenderTransactionId = txId;
                    TransactionCache.TryAdd(tx);

                    if (!alreadyKnown)
                    {
                        CoinSpent?.Invoke(this, foundCoin);
                    }

                    if (tx.Confirmed)
                    {
                        SpenderConfirmed?.Invoke(this, foundCoin);
                    }
                }
            }

            return(walletRelevant);
        }
Beispiel #12
0
 /// <summary>
 /// Dispose method for resource cleanup
 /// </summary>
 public void Dispose()
 {
     Header.Dispose();
     TransactionHashes.Dispose();
 }
 public GetTransactionInfosRequest(IEnumerable <string> transactionHashes)
 {
     TransactionHashes.AddRange(transactionHashes);
 }