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()));
            }
        }
Beispiel #2
0
        /// <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);
        }