public BlockChainInfo(IRpcMultiClient rpcMultiClient, ILogger <BlockChainInfo> logger, IEventBus eventBus, IClock clock) : base(logger, eventBus) { this.rpcMultiClient = rpcMultiClient ?? throw new ArgumentNullException(nameof(rpcMultiClient)); this.clock = clock ?? throw new ArgumentNullException(nameof(clock)); lastRefreshedAt = clock.UtcNow(); }
public BlockParser(IRpcMultiClient rpcMultiClient, ITxRepository txRepository, ILogger <BlockParser> logger, IEventBus eventBus, IOptions <AppSettings> options, IClock clock) : base(logger, eventBus) { this.rpcMultiClient = rpcMultiClient ?? throw new ArgumentNullException(nameof(rpcMultiClient)); this.txRepository = txRepository ?? throw new ArgumentNullException(nameof(txRepository)); this.clock = clock ?? throw new ArgumentNullException(nameof(clock)); appSettings = options.Value; }
public Mapi(IRpcMultiClient rpcMultiClient, IFeeQuoteRepository feeQuoteRepository, IBlockChainInfo blockChainInfo, IMinerId minerId, ITxRepository txRepository, ILogger <Mapi> logger, IClock clock) { this.rpcMultiClient = rpcMultiClient ?? throw new ArgumentNullException(nameof(rpcMultiClient)); this.feeQuoteRepository = feeQuoteRepository ?? throw new ArgumentNullException(nameof(feeQuoteRepository)); this.blockChainInfo = blockChainInfo ?? throw new ArgumentNullException(nameof(blockChainInfo)); this.minerId = minerId ?? throw new ArgumentException(nameof(minerId)); this.txRepository = txRepository ?? throw new ArgumentException(nameof(txRepository)); this.logger = logger ?? throw new ArgumentException(nameof(logger)); this.clock = clock ?? throw new ArgumentNullException(nameof(clock)); }
public DsntController(ITxRepository txRepository, IRpcMultiClient rpcMultiClient, ITransactionRequestsCheck transactionRequestsCheck, IHostBanList banList, IClock clock, IOptions <AppSettings> options, IEventBus eventBus, ILogger <DsntController> logger) { this.txRepository = txRepository ?? throw new ArgumentNullException(nameof(txRepository)); this.rpcMultiClient = rpcMultiClient ?? throw new ArgumentNullException(nameof(rpcMultiClient)); this.clock = clock ?? throw new ArgumentNullException(nameof(clock)); this.transactionRequestsCheck = transactionRequestsCheck ?? throw new ArgumentNullException(nameof(transactionRequestsCheck)); this.banList = banList ?? throw new ArgumentNullException(nameof(banList)); this.eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); appSettings = options.Value; }
public NotificationsHandler(ILogger <NotificationsHandler> logger, INotificationServiceHttpClientFactory httpClientFactory, IOptions <AppSettings> options, ITxRepository txRepository, IRpcMultiClient rpcMultiClient, IMinerId minerId, IClock clock) { this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); this.txRepository = txRepository ?? throw new ArgumentNullException(nameof(txRepository)); this.rpcMultiClient = rpcMultiClient ?? throw new ArgumentNullException(nameof(rpcMultiClient)); this.minerId = minerId ?? throw new ArgumentNullException(nameof(minerId)); notificationSettings = options.Value.Notification; var maxNumberOfSlowNotifications = notificationSettings.InstantNotificationsQueueSize.Value * notificationSettings.InstantNotificationsSlowTaskPercentage / 100; notificationScheduler = new NotificationScheduler(logger, maxNumberOfSlowNotifications, notificationSettings.InstantNotificationsQueueSize.Value, notificationSettings.MaxNotificationsInBatch.Value, notificationSettings.NoOfSavedExecutionTimes.Value, notificationSettings.SlowHostThresholdInMs.Value); this.clock = clock ?? throw new ArgumentNullException(nameof(clock)); }
/// <summary> /// Collect previous outputs being spent by tx. Two sources are consulted /// - batch of incoming transactions - outputs wil be found there in case of chained transactions /// - node /// Exception is thrown if output can not be found or is already spent on node side /// This function does not check for single output that is spent multiple times inside additionalTx. /// This will be detected by the node itself and one of the transactions spending the output will be rejected /// </summary> /// <param name="tx"></param> /// <param name="additionalTxs">optional </param> /// <param name="rpcMultiClient"></param> /// <returns> /// sum of al outputs being spent /// array of all outputs sorted in the same order as tx.inputs /// </returns> public static async Task <(Money sumPrevOuputs, PrevOut[] prevOuts)> CollectPreviousOuputs(Transaction tx, IReadOnlyDictionary <uint256, byte[]> additionalTxs, IRpcMultiClient rpcMultiClient) { var parentTransactionsFromBatch = new Dictionary <uint256, Transaction>(); var prevOutsNotInBatch = new List <OutPoint>(tx.Inputs.Count); foreach (var input in tx.Inputs) { var prevOut = input.PrevOut; if (parentTransactionsFromBatch.ContainsKey(prevOut.Hash)) { continue; } // First try to find the output in batch of transactions we are submitting if (additionalTxs != null && additionalTxs.TryGetValue(prevOut.Hash, out var txRaw)) { if (TryParseTransaction(txRaw, out var t)) { parentTransactionsFromBatch.TryAdd(prevOut.Hash, t); continue; } else { // Ignore parse errors. We might be able to get it from node. } } prevOutsNotInBatch.Add(prevOut); } Dictionary <OutPoint, PrevOut> prevOutsFromNode = null; // Fetch missing outputs from node if (prevOutsNotInBatch.Any()) { var missing = prevOutsNotInBatch.Select(x => (txId: x.Hash.ToString(), N: (long)x.N)).ToArray(); var prevOutsFromNodeResult = await rpcMultiClient.GetTxOutsAsync(missing, getTxOutFields); if (missing.Length != prevOutsFromNodeResult.TxOuts.Length) { throw new Exception( $"Internal error. Gettxouts RPC call should return exactly {missing.Length} elements, but it returned {prevOutsFromNodeResult.TxOuts.Length}"); } // Convert results to dictionary for faster lookup. // Responses are returned in same order as requests were passed in, so we can use Zip() to merge them prevOutsFromNode = new Dictionary <OutPoint, PrevOut>( prevOutsNotInBatch.Zip( prevOutsFromNodeResult.TxOuts, (K, V) => new KeyValuePair <OutPoint, PrevOut>(K, V)) ); } Money sumPrevOuputs = Money.Zero; var resultPrevOuts = new List <PrevOut>(tx.Inputs.Count); foreach (var input in tx.Inputs) { var outPoint = input.PrevOut; PrevOut prevOut; // Check if UTXO is present in batch of incoming transactions if (parentTransactionsFromBatch.TryGetValue(outPoint.Hash, out var txFromBatch)) { // we have found the input in input batch var outputs = txFromBatch.Outputs; if (outPoint.N > outputs.Count - 1) { prevOut = new PrevOut { Error = "Missing inputs - invalid output index" }; } else { var output = outputs[outPoint.N]; prevOut = new PrevOut { Error = null, // ScriptPubKey = null, // We do not use ScriptPUbKey ScriptPubKeyLength = output.ScriptPubKey.Length, Value = output.Value.ToDecimal(MoneyUnit.BTC), IsStandard = true, Confirmations = 0 }; } } else // ask the node for previous output { if (prevOutsFromNode == null || !prevOutsFromNode.TryGetValue(outPoint, out prevOut)) { // This indicates internal error in node or mAPI throw new Exception($"Node did not return output {outPoint} that we have asked for"); } } if (string.IsNullOrEmpty(prevOut.Error)) { sumPrevOuputs += new Money((long)(prevOut.Value * Money.COIN)); } resultPrevOuts.Add(prevOut); } return(sumPrevOuputs, resultPrevOuts.ToArray()); }