/// <inheritdoc /> public AddressBalancesResult GetAddressBalances(string[] addresses, int minConfirmations = 1) { var(isQueryable, reason) = this.IsQueryable(); if (!isQueryable) { return(AddressBalancesResult.RequestFailed(reason)); } var result = new AddressBalancesResult(); lock (this.lockObject) { foreach (var address in addresses) { AddressIndexerData indexData = this.addressIndexRepository.GetOrCreateAddress(address); int maxAllowedHeight = this.consensusManager.Tip.Height - minConfirmations + 1; long balance = indexData.BalanceChanges.Where(x => x.BalanceChangedHeight <= maxAllowedHeight).CalculateBalance(); this.logger.LogTrace("Address: {0}, balance: {1}.", address, balance); result.Balances.Add(new AddressBalanceResult(address, new Money(balance))); } return(result); } }
public IActionResult GetAddressBalance(string address) { long money = -1; try { AddressBalancesResult result = this.addressIndexer.GetAddressBalances(new[] { address }, 1); money = result.Balances.First().Balance.Satoshi; } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); } if (this.Request.Headers["Accept"] == "application/json") { GetBalanceResponseModel response = new GetBalanceResponseModel() { Balance = money }; return(Ok(response)); } else { return(Ok(money)); } }
public long GetAddressBalance(string address) { try { AddressBalancesResult result = this.addressIndexer.GetAddressBalances(new [] { address }, 1); return(result.Balances.First().Balance.Satoshi); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(-1); } }
public IActionResult GetAddressesBalances(string addresses, int minConfirmations) { try { string[] addressesArray = addresses.Split(','); this.logger.LogDebug("Asking data for {0} addresses.", addressesArray.Length); AddressBalancesResult result = this.addressIndexer.GetAddressBalances(addressesArray, minConfirmations); this.logger.LogDebug("Sending data for {0} addresses.", result.Balances.Count); return(this.Json(result)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
private async Task ProcessBlockForSwapVoteTransactionsAsync(BlockTransactionDetailsModel block, int blockHeight) { // Inspect each transaction foreach (TransactionVerboseModel transaction in block.Transactions) { // Find the first the OP_RETURN output. Vout opReturnOutput = transaction.VOut.FirstOrDefault(v => v.ScriptPubKey.Type == "nulldata"); if (opReturnOutput == null) { continue; } IList <Op> ops = new NBitcoin.Script(opReturnOutput.ScriptPubKey.Asm).ToOps(); var potentialVote = Encoding.ASCII.GetString(ops.Last().PushData); try { var isVote = potentialVote.Substring(0, 1); if (isVote != "V") { continue; } var isVoteValue = potentialVote.Substring(1, 1); if (isVoteValue == "1" || isVoteValue == "0") { // Verify the sender address is a valid Strat address var potentialStratAddress = potentialVote.Substring(2); ValidatedAddress validateResult = await $"http://localhost:{this.StratisNetworkApiPort}/api" .AppendPathSegment("node/validateaddress") .SetQueryParams(new { address = potentialStratAddress }) .GetJsonAsync <ValidatedAddress>(); if (!validateResult.IsValid) { Console.WriteLine($"Invalid STRAT address: '{potentialStratAddress}'"); continue; } AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api" .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}") .SetQueryParams(new { addresses = potentialStratAddress, minConfirmations = 0 }) .GetJsonAsync <AddressBalancesResult>(); if (isVoteValue == "0") { this.castVotes.Add(new CastVote() { Address = potentialStratAddress, Balance = balance.Balances[0].Balance, InFavour = false, BlockHeight = blockHeight }); Console.WriteLine($"'No' vote found at height {blockHeight}."); } if (isVoteValue == "1") { this.castVotes.Add(new CastVote() { Address = potentialStratAddress, Balance = balance.Balances[0].Balance, InFavour = true, BlockHeight = blockHeight }); Console.WriteLine($"'Yes' vote found at height {blockHeight}."); } } } catch (Exception) { } } }
private async Task ProcessBlockForCollateralVoteTransactionsAsync(BlockTransactionDetailsModel block, int blockHeight) { // Inspect each transaction foreach (TransactionVerboseModel transaction in block.Transactions) { // Find the first the OP_RETURN output. Vout opReturnOutput = transaction.VOut.FirstOrDefault(v => v.ScriptPubKey.Type == "nulldata"); if (opReturnOutput == null) { continue; } // Before checking if it's a vote, check if it's a swap transaction as we now know it has an OP_RETURN. // Ignore any transactions that have inconsequential OP_RETURN values. if (opReturnOutput.Value >= 1.0m) { // For the purposes of speeding up this search, it doesn't matter per se whether the burn transaction has a valid destination address in it. TransactionModel tx = this.blockExplorerClient.GetTransaction(transaction.TxId); // Check the inputs of the transaction to see if it was funded by one of the vote addresses. string address = null; foreach (In input in tx._in) { if (input.hash == null) { continue; } // The block explorer calls this 'hash' but it is in fact the address that funded the input. // It is possible that several voting addresses consolidated together in one swap transaction, so just take the first one. if (this.collateralVotes.ContainsKey(input.hash)) { if (address == null) { // We will assign the entire burn amount to the first found voting address. address = input.hash; } else { // However, we have to recompute the balance of all the vote addresses present in the inputs that we are not going to assign the burn to. // This is because they will not necessarily be revisited later if there are no further transactions affecting them. // We presume that since they are participating in a burn subsequent to their initial vote, their balance will drop. if (!address.Equals(input.hash)) { AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api" .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}") .SetQueryParams(new { addresses = input.hash, minConfirmations = 0 }) .GetJsonAsync <AddressBalancesResult>(); Console.WriteLine($"Reset balance for '{input.hash}' to {balance.Balances[0].Balance} due to burn transaction {transaction.TxId} at height {blockHeight}"); this.collateralVotes[input.hash].Balance = balance.Balances[0].Balance; } } } } if (address != null) { this.collateralVotes[address].BlockHeight = blockHeight; this.collateralVotes[address].Balance = Money.Coins(opReturnOutput.Value); Console.WriteLine($"Detected that address '{address}' burnt {opReturnOutput.Value} via transaction {transaction.TxId} at height {blockHeight}"); // We can now skip checking if this output was a vote. continue; } } IList <Op> ops = new NBitcoin.Script(opReturnOutput.ScriptPubKey.Asm).ToOps(); var potentialVote = Encoding.ASCII.GetString(ops.Last().PushData); try { var isVote = potentialVote.Substring(0, 1); if (isVote != "V") { continue; } var isCollateralVote = potentialVote.Substring(1, 1); if (isCollateralVote != "C") { continue; } var collateralVote = potentialVote.Substring(2, 1); if (!new[] { "A", "B", "C", "D", "E" }.Contains(collateralVote)) { Console.WriteLine($"Invalid vote found '{collateralVote}'; height {blockHeight}."); continue; } // Verify the sender address is a valid Strat address var potentialStratAddress = potentialVote.Substring(3); ValidatedAddress validateResult = await $"http://localhost:{this.StratisNetworkApiPort}/api" .AppendPathSegment("node/validateaddress") .SetQueryParams(new { address = potentialStratAddress }) .GetJsonAsync <ValidatedAddress>(); if (!validateResult.IsValid) { Console.WriteLine($"Invalid STRAT address: '{potentialStratAddress}'"); continue; } AddressBalancesResult balance = await $"http://localhost:{this.StratisNetworkApiPort}/api" .AppendPathSegment($"blockstore/{BlockStoreRouteEndPoint.GetAddressesBalances}") .SetQueryParams(new { addresses = potentialStratAddress, minConfirmations = 0 }) .GetJsonAsync <AddressBalancesResult>(); Money determinedBalance = balance.Balances[0].Balance; if (!this.collateralVotes.ContainsKey(potentialStratAddress)) { Console.WriteLine($"Collateral vote found for {potentialStratAddress} at height {blockHeight}; Selection '{collateralVote}'; Balance {determinedBalance}"); this.collateralVotes.Add(potentialStratAddress, new CollateralVote() { Address = potentialStratAddress, Balance = determinedBalance, Selection = collateralVote, BlockHeight = blockHeight }); } else { Console.WriteLine($"Updating existing vote for {potentialStratAddress} at height {blockHeight}; Selection '{collateralVote}'; Balance {determinedBalance}"); this.collateralVotes[potentialStratAddress] = new CollateralVote() { Address = potentialStratAddress, Balance = determinedBalance, Selection = collateralVote, BlockHeight = blockHeight }; } } catch (Exception) { } } }