public async Task <IActionResult> GetMaturedBlockDepositsAsync([FromBody] MaturedBlockRequestModel blockRequest) { Guard.NotNull(blockRequest, nameof(blockRequest)); if (!this.ModelState.IsValid) { IEnumerable <string> errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage)); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors))); } try { Result <List <MaturedBlockDepositsModel> > depositsResult = await this.maturedBlocksProvider.GetMaturedDepositsAsync( blockRequest.BlockHeight, blockRequest.MaxBlocksToSend).ConfigureAwait(false); if (depositsResult.IsSuccess) { return(this.Json(depositsResult.Value)); } this.logger.LogDebug("Error calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetMaturedBlockDeposits, depositsResult.Error); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"Could not re-sync matured block deposits: {depositsResult.Error}", depositsResult.Error)); } catch (Exception e) { this.logger.LogDebug("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetMaturedBlockDeposits, e.Message); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"Could not re-sync matured block deposits: {e.Message}", e.ToString())); } }
/// <summary>Asks for blocks from another gateway node and then processes them.</summary> /// <returns><c>true</c> if delay between next time we should ask for blocks is required; <c>false</c> otherwise.</returns> /// <exception cref="OperationCanceledException">Thrown when <paramref name="cancellationToken"/> is cancelled.</exception> protected async Task <bool> SyncBatchOfBlocksAsync(CancellationToken cancellationToken = default(CancellationToken)) { int blocksToRequest = 1; // TODO why are we asking for max of 1 block and if it's not suspended then 1000? investigate this logic in maturedBlocksProvider if (!this.store.HasSuspended()) { blocksToRequest = MaxBlocksToRequest; } // API method that provides blocks should't give us blocks that are not mature! var model = new MaturedBlockRequestModel(this.store.NextMatureDepositHeight, blocksToRequest); this.logger.LogDebug("Request model created: {0}:{1}, {2}:{3}.", nameof(model.BlockHeight), model.BlockHeight, nameof(model.MaxBlocksToSend), model.MaxBlocksToSend); // Ask for blocks. IList <MaturedBlockDepositsModel> matureBlockDeposits = await this.federationGatewayClient.GetMaturedBlockDepositsAsync(model, cancellationToken).ConfigureAwait(false); bool delayRequired = true; if (matureBlockDeposits != null) { // Log what we've received. foreach (MaturedBlockDepositsModel maturedBlockDeposit in matureBlockDeposits) { // Order transactions in block deterministically maturedBlockDeposit.Deposits = maturedBlockDeposit.Deposits.OrderBy(x => x.Id, Comparer <uint256> .Create(DeterministicCoinOrdering.CompareUint256)).ToList(); foreach (IDeposit deposit in maturedBlockDeposit.Deposits) { this.logger.LogDebug("New deposit received BlockNumber={0}, TargetAddress='{1}', depositId='{2}', Amount='{3}'.", deposit.BlockNumber, deposit.TargetAddress, deposit.Id, deposit.Amount); } } if (matureBlockDeposits.Count > 0) { bool success = await this.store.RecordLatestMatureDepositsAsync(matureBlockDeposits).ConfigureAwait(false); // If we received a portion of blocks we can ask for new portion without any delay. if (success) { delayRequired = false; } } else { this.logger.LogDebug("Considering ourselves fully synced since no blocks were received"); // If we've received nothing we assume we are at the tip and should flush. // Same mechanic as with syncing headers protocol. await this.store.SaveCurrentTipAsync().ConfigureAwait(false); } } return(delayRequired); }
public void ShouldSerialiseAsJson() { var maturedBlockDeposits = new MaturedBlockRequestModel(TestingValues.GetPositiveInt(), TestingValues.GetPositiveInt()); var asJson = maturedBlockDeposits.ToString(); var reconverted = JsonConvert.DeserializeObject <MaturedBlockRequestModel>(asJson); reconverted.BlockHeight.Should().Be(maturedBlockDeposits.BlockHeight); reconverted.MaxBlocksToSend.Should().Be(maturedBlockDeposits.MaxBlocksToSend); }
/// <inheritdoc /> public async Task <bool> GetMoreBlocksAsync() { if (!this.CanSend()) { return(false); } int maxBlocksToRequest = 1; if (!this.crossChainTransferStore.HasSuspended()) { maxBlocksToRequest = MaxBlocksToCatchup; } var model = new MaturedBlockRequestModel(this.crossChainTransferStore.NextMatureDepositHeight, maxBlocksToRequest); HttpResponseMessage response = await this.SendAsync(model, FederationGatewayRouteEndPoint.GetMaturedBlockDeposits).ConfigureAwait(false); if (response.IsSuccessStatusCode) { string successJson = response.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); if (successJson != null) { var blockDeposits = JsonConvert.DeserializeObject <MaturedBlockDepositsModel[]>(successJson); if (blockDeposits.Length > 0) { this.maturedBlockReceiver.ReceiveMaturedBlockDeposits(blockDeposits); if (blockDeposits.Length < maxBlocksToRequest) { await this.crossChainTransferStore.SaveCurrentTipAsync().ConfigureAwait(false); } } return(true); } } return(false); }
/// <inheritdoc /> public Task <List <MaturedBlockDepositsModel> > GetMaturedBlockDepositsAsync(MaturedBlockRequestModel model, CancellationToken cancellation = default(CancellationToken)) { return(this.SendPostRequestAsync <MaturedBlockRequestModel, List <MaturedBlockDepositsModel> >(model, FederationGatewayRouteEndPoint.GetMaturedBlockDeposits, cancellation)); }
/// <summary>Asks for blocks from another gateway node and then processes them.</summary> /// <returns><c>true</c> if delay between next time we should ask for blocks is required; <c>false</c> otherwise.</returns> /// <exception cref="OperationCanceledException">Thrown when <paramref name="cancellationToken"/> is cancelled.</exception> protected async Task <bool> SyncBatchOfBlocksAsync(CancellationToken cancellationToken = default(CancellationToken)) { int blocksToRequest = 1; // TODO why are we asking for max of 1 block and if it's not suspended then 1000? investigate this logic in maturedBlocksProvider if (!this.store.HasSuspended()) { blocksToRequest = MaxBlocksToRequest; } // API method that provides blocks should't give us blocks that are not mature! var model = new MaturedBlockRequestModel(this.store.NextMatureDepositHeight, blocksToRequest, MaxDepositsToRequest); this.logger.LogDebug("Request model created: {0}:{1}, {2}:{3}.", nameof(model.BlockHeight), model.BlockHeight, nameof(model.MaxBlocksToSend), model.MaxBlocksToSend); // Ask for blocks. SerializableResult <List <MaturedBlockDepositsModel> > matureBlockDepositsResult = await this.federationGatewayClient.GetMaturedBlockDepositsAsync(model, cancellationToken).ConfigureAwait(false); if (matureBlockDepositsResult == null) { this.logger.LogDebug("Failed to fetch matured block deposits from counter chain node; {0} didn't respond.", this.federationGatewayClient.EndpointUrl); this.logger.LogTrace("(-)[COUNTERCHAIN_NODE_NO_RESPONSE]:true"); return(true); } if (matureBlockDepositsResult.Value == null) { this.logger.LogDebug("Failed to fetch matured block deposits from counter chain node; {0} didn't reply with any deposits; Message: {1}", this.federationGatewayClient.EndpointUrl, matureBlockDepositsResult.Message ?? "none"); this.logger.LogTrace("(-)[COUNTERCHAIN_NODE_SENT_NO_DEPOSITS]:true"); return(true); } bool delayRequired = true; // Log what we've received. foreach (MaturedBlockDepositsModel maturedBlockDeposit in matureBlockDepositsResult.Value) { // Order transactions in block deterministically maturedBlockDeposit.Deposits = maturedBlockDeposit.Deposits.OrderBy(x => x.Id, Comparer <uint256> .Create(DeterministicCoinOrdering.CompareUint256)).ToList(); foreach (IDeposit deposit in maturedBlockDeposit.Deposits) { this.logger.LogDebug("New deposit received BlockNumber={0}, TargetAddress='{1}', depositId='{2}', Amount='{3}'.", deposit.BlockNumber, deposit.TargetAddress, deposit.Id, deposit.Amount); } } if (matureBlockDepositsResult.Value.Count > 0) { RecordLatestMatureDepositsResult result = await this.store.RecordLatestMatureDepositsAsync(matureBlockDepositsResult.Value).ConfigureAwait(false); // If we received a portion of blocks we can ask for new portion without any delay. if (result.MatureDepositRecorded) { delayRequired = false; } } else { this.logger.LogDebug("Considering ourselves fully synced since no blocks were received."); // If we've received nothing we assume we are at the tip and should flush. // Same mechanic as with syncing headers protocol. await this.store.SaveCurrentTipAsync().ConfigureAwait(false); } return(delayRequired); }