/// <inheritdoc /> public VerboseAddressBalancesResult GetVerboseAddressBalancesData(string[] addresses) { (bool isQueryable, string reason) = this.IsQueryable(); if (!isQueryable) { return(VerboseAddressBalancesResult.RequestFailed(reason)); } var result = new VerboseAddressBalancesResult(); lock (this.lockObject) { foreach (var address in addresses) { AddressIndexerData indexData = this.addressIndexRepository.GetOrCreateAddress(address); var copy = new AddressIndexerData() { Address = indexData.Address, BalanceChanges = new List <AddressBalanceChange>(indexData.BalanceChanges) }; result.BalancesData.Add(copy); } } result.ConsensusTipHeight = this.consensusManager.Tip.Height; return(result); }
/// <inheritdoc /> public VerboseAddressBalancesResult GetAddressIndexerState(string[] addresses) { var result = new VerboseAddressBalancesResult(this.consensusManager.Tip.Height); if (addresses.Length == 0) { return(result); } var(isQueryable, reason) = IsQueryable(); if (!isQueryable) { return(VerboseAddressBalancesResult.RequestFailed(reason)); } lock (this.lockObject) { foreach (var address in addresses) { var indexData = this.addressIndexRepository.GetOrCreateAddress(address); var copy = new AddressIndexerData { Address = indexData.Address, BalanceChanges = new List <AddressBalanceChange>(indexData.BalanceChanges) }; result.BalancesData.Add(copy); } } return(result); }
private async Task UpdateCollateralInfoAsync(CancellationToken cancellation) { List <string> addressesToCheck; lock (this.locker) { addressesToCheck = this.balancesDataByAddress.Keys.ToList(); } if (addressesToCheck.Count == 0) { this.collateralUpdated = true; this.logger.LogInformation("None of the federation members has a collateral requirement configured."); this.logger.LogTrace("(-)[NOTHING_TO_CHECK]:true"); return; } this.logger.LogDebug("Addresses to check {0}.", addressesToCheck.Count); VerboseAddressBalancesResult verboseAddressBalanceResult = await this.blockStoreClient.GetVerboseAddressesBalancesDataAsync(addressesToCheck, cancellation).ConfigureAwait(false); if (verboseAddressBalanceResult == null) { this.logger.LogWarning("Failed to update collateral, please ensure that the mainnet gateway node is running and it's API feature is enabled."); this.logger.LogTrace("(-)[CALL_RETURNED_NULL_RESULT]:false"); return; } if (!string.IsNullOrEmpty(verboseAddressBalanceResult.Reason)) { this.logger.LogWarning("Failed to fetch address balances from the counter chain node : {0}", verboseAddressBalanceResult.Reason); this.logger.LogTrace("(-)[FAILED]:{0}", verboseAddressBalanceResult.Reason); return; } this.logger.LogDebug("Addresses received {0}.", verboseAddressBalanceResult.BalancesData.Count); if (verboseAddressBalanceResult.BalancesData.Count != addressesToCheck.Count) { this.logger.LogDebug("Expected {0} data entries but received {1}.", addressesToCheck.Count, verboseAddressBalanceResult.BalancesData.Count); this.logger.LogTrace("(-)[CALL_RETURNED_INCONSISTENT_DATA]:false"); return; } lock (this.locker) { foreach (AddressIndexerData balanceData in verboseAddressBalanceResult.BalancesData) { this.logger.LogDebug("Updating federation member address {0}.", balanceData.Address); this.balancesDataByAddress[balanceData.Address] = balanceData; } this.counterChainConsensusTipHeight = verboseAddressBalanceResult.ConsensusTipHeight; } this.collateralUpdated = true; }
public IActionResult GetVerboseAddressesBalancesData(string addresses) { try { string[] addressesArray = addresses?.Split(',') ?? new string[] { }; this.logger.LogDebug("Asking data for {0} addresses.", addressesArray.Length); VerboseAddressBalancesResult result = this.addressIndexer.GetAddressIndexerState(addressesArray); return(this.Json(result)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
/// <inheritdoc /> public VerboseAddressBalancesResult GetAddressIndexerState(string[] addresses) { // If the containing feature is not initialized then wait a bit. this.InitializingFeature?.WaitInitialized(); var result = new VerboseAddressBalancesResult(this.consensusManager.Tip.Height); if (addresses.Length == 0) { return(result); } if (!this.storeSettings.AddressIndex) { throw new NotSupportedException("Address indexing is not enabled."); } (bool isQueryable, string reason) = this.IsQueryable(); if (!isQueryable) { return(VerboseAddressBalancesResult.RequestFailed(reason)); } lock (this.lockObject) { foreach (var address in addresses) { AddressIndexerData indexData = this.addressIndexRepository.GetOrCreateAddress(address); var copy = new AddressIndexerData() { Address = indexData.Address, BalanceChanges = new List <AddressBalanceChange>(indexData.BalanceChanges) }; result.BalancesData.Add(copy); } } return(result); }
public async Task CanInitializeAndCheckCollateralAsync() { var blockStoreClientMock = new Mock <IBlockStoreClient>(); var collateralData = new VerboseAddressBalancesResult(this.collateralCheckHeight + 1000) { BalancesData = new List <AddressIndexerData>() { new AddressIndexerData() { Address = this.collateralFederationMembers[0].CollateralMainchainAddress, BalanceChanges = new List <AddressBalanceChange>() { new AddressBalanceChange() { BalanceChangedHeight = 0, Deposited = true, Satoshi = this.collateralFederationMembers[0].CollateralAmount } } }, new AddressIndexerData() { Address = this.collateralFederationMembers[1].CollateralMainchainAddress, BalanceChanges = new List <AddressBalanceChange>() { new AddressBalanceChange() { BalanceChangedHeight = 0, Deposited = true, Satoshi = this.collateralFederationMembers[1].CollateralAmount + 10 } } }, new AddressIndexerData() { Address = this.collateralFederationMembers[2].CollateralMainchainAddress, BalanceChanges = new List <AddressBalanceChange>() { new AddressBalanceChange() { BalanceChangedHeight = 0, Deposited = true, Satoshi = this.collateralFederationMembers[2].CollateralAmount - 10 } } } } }; blockStoreClientMock.Setup(x => x.GetVerboseAddressesBalancesDataAsync(It.IsAny <IEnumerable <string> >(), It.IsAny <CancellationToken>())).ReturnsAsync(collateralData); this.collateralChecker.SetPrivateVariableValue("blockStoreClient", blockStoreClientMock.Object); await this.collateralChecker.InitializeAsync(); Assert.True(this.collateralChecker.CheckCollateral(this.collateralFederationMembers[0], this.collateralCheckHeight)); Assert.True(this.collateralChecker.CheckCollateral(this.collateralFederationMembers[1], this.collateralCheckHeight)); Assert.False(this.collateralChecker.CheckCollateral(this.collateralFederationMembers[2], this.collateralCheckHeight)); // Now change what the client returns and make sure collateral check fails after update. AddressIndexerData updated = collateralData.BalancesData.First(b => b.Address == this.collateralFederationMembers[0].CollateralMainchainAddress); updated.BalanceChanges.First().Satoshi = this.collateralFederationMembers[0].CollateralAmount - 1; // Wait CollateralUpdateIntervalSeconds + 1 seconds await Task.Delay(21_000); Assert.False(this.collateralChecker.CheckCollateral(this.collateralFederationMembers[0], this.collateralCheckHeight)); this.collateralChecker.Dispose(); }
public GetUTXOsResponseModel GetUTXOsForAddress([FromQuery] string address) { VerboseAddressBalancesResult balancesResult = this.addressIndexer.GetAddressIndexerState(new[] { address }); if (balancesResult.BalancesData == null || balancesResult.BalancesData.Count != 1) { this.logger.LogWarning("No balances found for address {0}, Reason: {1}", address, balancesResult.Reason); return(new GetUTXOsResponseModel() { Reason = balancesResult.Reason }); } BitcoinAddress bitcoinAddress = this.network.CreateBitcoinAddress(address); AddressIndexerData addressBalances = balancesResult.BalancesData.First(); List <AddressBalanceChange> deposits = addressBalances.BalanceChanges.Where(x => x.Deposited).ToList(); long totalDeposited = deposits.Sum(x => x.Satoshi); long totalWithdrawn = addressBalances.BalanceChanges.Where(x => !x.Deposited).Sum(x => x.Satoshi); long balanceSat = totalDeposited - totalWithdrawn; List <int> heights = deposits.Select(x => x.BalanceChangedHeight).Distinct().ToList(); HashSet <uint256> blocksToRequest = new HashSet <uint256>(heights.Count); foreach (int height in heights) { uint256 blockHash = this.chainState.ConsensusTip.GetAncestor(height).Header.GetHash(); blocksToRequest.Add(blockHash); } List <Block> blocks = this.blockStore.GetBlocks(blocksToRequest.ToList()); List <OutPoint> collectedOutPoints = new List <OutPoint>(deposits.Count); foreach (List <Transaction> txList in blocks.Select(x => x.Transactions)) { foreach (Transaction transaction in txList.Where(x => !x.IsCoinBase && !x.IsCoinStake)) { for (int i = 0; i < transaction.Outputs.Count; i++) { if (!transaction.Outputs[i].IsTo(bitcoinAddress)) { continue; } collectedOutPoints.Add(new OutPoint(transaction, i)); } } } FetchCoinsResponse fetchCoinsResponse = this.coinView.FetchCoins(collectedOutPoints.ToArray()); GetUTXOsResponseModel response = new GetUTXOsResponseModel() { BalanceSat = balanceSat, UTXOs = new List <UTXOModel>() }; foreach (KeyValuePair <OutPoint, UnspentOutput> unspentOutput in fetchCoinsResponse.UnspentOutputs) { if (unspentOutput.Value.Coins == null) { continue; // spent } OutPoint outPoint = unspentOutput.Key; Money value = unspentOutput.Value.Coins.TxOut.Value; response.UTXOs.Add(new UTXOModel(outPoint, value)); } return(response); }