public static void Validate(IReadOnlyCollection <Transfer> transfers) { if (transfers == null || !transfers.Any()) { throw RequestValidationException.ShouldBeNotEmptyCollection(nameof(transfers)); } var duplicatedTransfers = transfers .GroupBy(x => new { x.Asset, x.SourceAddress, x.DestinationAddress }) .Where(x => x.Count() > 1) .ToArray(); if (duplicatedTransfers.Any()) { var duplicatesMessage = new StringBuilder(); duplicatesMessage.AppendLine(); foreach (var group in duplicatedTransfers) { duplicatesMessage.AppendLine($"Asset: {group.Key.Asset}, source address: {group.Key.SourceAddress}, destination address: {group.Key.DestinationAddress}"); } throw new RequestValidationException( $"Only one transfer per asset, source address, destination address is allowed. Duplicates are: {duplicatesMessage}", nameof(transfers)); } }
/// <summary> /// Coin to receive for the transaction. /// </summary> /// <param name="coinNumber">Number of the coin inside the transaction.</param> /// <param name="asset">Asset of the coin.</param> /// <param name="value">Coin value to receive.</param> /// <param name="address">Address which should receive the coin.</param> /// <param name="addressTag"> /// Optional. /// Receiving address tag. /// </param> /// <param name="addressTagType"> /// Optional. /// Type of the receiving address tag. /// </param> public CoinToReceive( int coinNumber, Asset asset, UMoney value, Address address, AddressTag addressTag = null, AddressTagType?addressTagType = null) { if (coinNumber < 0) { throw RequestValidationException.ShouldBeZeroOrPositiveNumber(coinNumber, nameof(coinNumber)); } if (addressTagType.HasValue && addressTag == null) { throw new RequestValidationException("If the tag type is specified, the tag should be specified too", new [] { nameof(addressTagType), nameof(addressTag) }); } CoinNumber = coinNumber; Asset = asset ?? throw RequestValidationException.ShouldBeNotNull(nameof(asset)); Value = value; Address = address ?? throw RequestValidationException.ShouldBeNotNull(nameof(address)); AddressTag = addressTag; AddressTagType = addressTagType; }
/// <summary> /// Endpoint: [POST] /api/addresses /// </summary> /// <param name="encryptionPublicKey"> /// Encryption public key which should be used to encrypt /// the private key of the address being created before /// return it in the encryptedPrivateKey field of the /// response. /// </param> public CreateAddressRequest(Base64String encryptionPublicKey) { if (string.IsNullOrWhiteSpace(encryptionPublicKey?.ToString())) { throw RequestValidationException.ShouldBeNotEmptyString(nameof(encryptionPublicKey)); } EncryptionPublicKey = encryptionPublicKey; }
/// <summary> /// Endpoint: [POST] /api/transactions/broadcasted /// </summary> /// <param name="signedTransaction">The signed transaction.</param> public BroadcastTransactionRequest(Base64String signedTransaction) { if (string.IsNullOrWhiteSpace(signedTransaction?.ToString())) { throw RequestValidationException.ShouldBeNotEmptyString(nameof(signedTransaction)); } SignedTransaction = signedTransaction; }
public async Task <ActionResult <TransactionStateResponse> > GetState([FromRoute] TransactionId transactionId) { if (transactionId == null) { throw RequestValidationException.ShouldBeNotNull(nameof(transactionId)); } var state = await _transactionsStateProvider.GetStateAsync(transactionId); return(Ok(new TransactionStateResponse(state))); }
/// <summary> /// Build response for <see cref="RequestValidationException"/>. /// </summary> /// <param name="ex">Exception information about an error that happened in the WebApi project.</param> private static Task BuildAndSendAsync(RequestValidationException ex, ErrorBuilder errorBuilder) { foreach (var error in ex.Errors) { errorBuilder.AddData($"Invalid property: {error.PropertyName}", string.Join("; ", error.Messages)); } return(errorBuilder .SetDescription("Invalid request parameters") .SetHttpCode(HttpStatusCode.BadRequest) .SetErrorCode("RequestValidationError") .BuildAndSendAsync()); }
public void GivenExceptionIsRequestValidationException_WhenRunningPipeline_ThenReturnsBadRequestWithExceptionDetails() { var requestValidationException = new RequestValidationException("Exception info 1"); var expectedExceptionModel = requestValidationException.Message; var result = _sut.Invoke(A.Dummy <NancyContext>(), requestValidationException) as Negotiator; result.Should().NotBeNull(); result.NegotiationContext.StatusCode.Should().Be(HttpStatusCode.BadRequest); var actualExceptionModel = result.NegotiationContext.GetModelForMediaRange("application/json") as object; actualExceptionModel.Should().BeAssignableTo <string>(); ((string)actualExceptionModel).ShouldBeEquivalentTo(expectedExceptionModel); }
private async Task HandleRequestValidationException(string correlation, RequestValidationException e) { _httpContextService.SetStatusCode(HttpStatusCode.NotImplemented); _httpContextService.TryAddHeader(CorrelationHeaderKey, correlation); if (_settings?.Gui?.EnableUserInterface == true) { var pageContents = StaticResources.stub_not_configured_html_page.Replace("[ROOT_URL]", _httpContextService.RootUrl); _httpContextService.AddHeader("Content-Type", Constants.HtmlMime); await _httpContextService.WriteAsync(pageContents); } _logger.LogInformation($"Request validation exception thrown: {e.Message}"); }
/// <summary> /// Transaction expiration options. /// </summary> /// <param name="afterMoment">Transaction should be expired after given moment.</param> /// <param name="afterBlockNumber">Transaction should be expired after given block number.</param> public ExpirationOptions(DateTime?afterMoment = null, long?afterBlockNumber = null) { if (!afterMoment.HasValue && !afterBlockNumber.HasValue) { throw new RequestValidationException("At least one option should be specified", new [] { nameof(afterMoment), nameof(afterBlockNumber) }); } if (afterBlockNumber < 1) { throw RequestValidationException.ShouldBePositiveNumber(afterBlockNumber, nameof(afterBlockNumber)); } AfterMoment = afterMoment; AfterBlockNumber = afterBlockNumber; }
/// <summary> /// Coin to spend for the transaction. /// </summary> /// <param name="coinId">Reference to the coin which should be spend.</param> /// <param name="asset">Asset of the coin.</param> /// <param name="value">Coin value to spend.</param> /// <param name="address">Address that owns the coin.</param> /// <param name="addressContext"> /// Optional. /// Address context associated with the owner address. /// </param> /// <param name="addressNonce"> /// Optional. /// Nonce number of the transaction for the owner address. /// </param> public CoinToSpend( CoinId coinId, Asset asset, UMoney value, Address address, Base64String addressContext = null, long?addressNonce = null) { Coin = coinId ?? throw RequestValidationException.ShouldBeNotNull(nameof(coinId)); Asset = asset ?? throw RequestValidationException.ShouldBeNotNull(nameof(asset)); Value = value; Address = address ?? throw RequestValidationException.ShouldBeNotNull(nameof(address)); AddressContext = addressContext; AddressNonce = addressNonce; }
/// <summary> /// Endpoint: [POST] /api/transactions/signed /// </summary> /// <param name="privateKeys"> /// Private keys of the addresses which should sign the transaction. /// Multiple keys can be used for the/ transactions with multiple inputs. /// </param> /// <param name="transactionContext"> /// Implementation specific transaction context. /// </param> public SignTransactionRequest(IReadOnlyCollection <EncryptedString> privateKeys, Base64String transactionContext) { if (privateKeys == null || !privateKeys.Any() || privateKeys.Any(x => x == null)) { throw RequestValidationException.ShouldBeNotEmptyCollection(nameof(privateKeys)); } if (string.IsNullOrWhiteSpace(transactionContext?.ToString())) { throw RequestValidationException.ShouldBeNotEmptyString(nameof(transactionContext)); } PrivateKeys = privateKeys; TransactionContext = transactionContext; }
public async Task <ActionResult <CreateAddressTagResponse> > CreateAddressTag([FromRoute] Address address, [FromBody] CreateAddressTagRequest request) { if (address == null) { throw RequestValidationException.ShouldBeNotNull(nameof(address)); } var response = await _addressGenerator.CreateAddressTagAsync(address, request); if (response == null) { throw new InvalidOperationException("Not null response object expected"); } return(Ok(response)); }
public async Task <ActionResult <AddressFormatsResponse> > GetFormats([FromRoute] Address address) { if (address == null) { throw RequestValidationException.ShouldBeNotNull(nameof(address)); } var response = await _addressFormatsProvider.GetFormatsAsync(address); if (response == null) { throw new InvalidOperationException("Not null response object expected"); } return(Ok(response)); }
public async Task <ActionResult <RawObjectResponse> > GetRaw([FromRoute] TransactionId transactionId) { if (transactionId == null) { throw RequestValidationException.ShouldBeNotNull(nameof(transactionId)); } var raw = await _rawObjectsRepository.GetOrDefaultAsync(RawObjectType.Transaction, transactionId); if (raw == null) { return(NotFound(BlockchainErrorResponse.Create($"Raw transaction [{transactionId}] not found"))); } return(Ok(new RawObjectResponse(raw))); }
/// <summary> /// Endpoint: [POST] /api/transactions/built/transfers/amount /// </summary> /// <param name="transfers">Transaction transfers.</param> /// <param name="fees">Fees amount in particular asset to spend for the given transaction.</param> /// <param name="expiration"> /// Optional. /// Transaction expiration options. Used if blockchain /// supports transaction expiration. If blockchain supports /// transaction expiration and the value is omitted, /// it should be interpreted as infinite expiration. /// If several expiration options are specified at once, /// and blockchain supports both of them, then transaction /// should be expired when earliest condition is met. /// </param> public BuildTransferAmountTransactionRequest( IReadOnlyCollection <Transfer> transfers, IReadOnlyCollection <Fee> fees, ExpirationOptions expiration = null) { if (fees == null) { throw RequestValidationException.ShouldBeNotNull(nameof(fees)); } TransactionTransfersValidator.Validate(transfers); FeesValidator.ValidateFeesInRequest(fees); Transfers = transfers; Fees = fees; Expiration = expiration; }
/// <summary> /// Translate certain "special" classes of exception to classes that can be passed through the service boundary. /// </summary> /// <param name="e"></param> /// <returns></returns> private static bool TranslateException(ref Exception e) { // special handling of EntityVersionException // assume all such exceptions occured because of concurrent modifications // wrap in ConcurrentModificationException will be used in the fault contract if (e is EntityVersionException) { e = new ConcurrentModificationException(e.Message); return(true); } // special handling of EntityValidationException // convert to RequestValidationException if (e is EntityValidationException) { e = new RequestValidationException(e.Message); return(true); } return(false); }
public async Task <ActionResult <AddressValidityResponse> > Validate( [FromRoute] Address address, [FromQuery] AddressTagType?tagType = null, [FromQuery] AddressTag tag = null) { if (address == null) { throw RequestValidationException.ShouldBeNotNull(nameof(address)); } if (tagType.HasValue && tag == null) { throw new RequestValidationException("If the tag type is specified, the tag should be specified too", new [] { nameof(tagType), nameof(tag) }); } var response = await _addressValidator.ValidateAsync(address, tagType, tag); if (response == null) { throw new InvalidOperationException("Not null response object expected"); } return(Ok(response)); }
/// <summary> /// Transfer of the transaction. /// </summary> /// <param name="asset">Asset to transfer.</param> /// <param name="amount">Amount to transfer from the source address to the destination address.</param> /// <param name="sourceAddress">Address to transfer from.</param> /// <param name="destinationAddress">Address to transfer to.</param> /// <param name="sourceAddressContext"> /// Optional. /// Source address context associated with the address. /// </param> /// <param name="sourceAddressNonce"> /// Optional. /// Nonce number of the transaction for the source address. /// </param> /// <param name="destinationAddressTag"> /// Optional. /// Destination address tag. /// </param> /// <param name="destinationAddressTagType"> /// Optional. /// Type of the destination address tag. /// </param> public Transfer( Asset asset, UMoney amount, Address sourceAddress, Address destinationAddress, Base64String sourceAddressContext = null, long?sourceAddressNonce = null, AddressTag destinationAddressTag = null, AddressTagType?destinationAddressTagType = null) { if (destinationAddressTagType.HasValue && destinationAddressTag == null) { throw new RequestValidationException("If the tag type is specified, the tag should be specified too", new [] { nameof(destinationAddressTagType), nameof(destinationAddressTag) }); } Asset = asset ?? throw RequestValidationException.ShouldBeNotNull(nameof(asset)); Amount = amount; SourceAddress = sourceAddress ?? throw RequestValidationException.ShouldBeNotNull(nameof(sourceAddress)); SourceAddressContext = sourceAddressContext; SourceAddressNonce = sourceAddressNonce; DestinationAddress = destinationAddress ?? throw RequestValidationException.ShouldBeNotNull(nameof(destinationAddress)); DestinationAddressTag = destinationAddressTag; DestinationAddressTagType = destinationAddressTagType; }
public static void Validate(IReadOnlyCollection <CoinToSpend> coinsToSpend, IReadOnlyCollection <CoinToReceive> coinsToReceive) { if (coinsToSpend == null || !coinsToSpend.Any()) { throw RequestValidationException.ShouldBeNotEmptyCollection(nameof(coinsToSpend)); } if (coinsToReceive == null || !coinsToReceive.Any()) { throw RequestValidationException.ShouldBeNotEmptyCollection(nameof(coinsToReceive)); } var coinsToReceiveNumbers = coinsToReceive.Select(x => x.CoinNumber).OrderBy(x => x).ToArray(); if (coinsToReceiveNumbers[0] > 0) { throw new RequestValidationException("Least coins to receive number should be 0", coinsToReceive.Select(x => x.CoinNumber), nameof(coinsToReceive)); } var previousCoinToReceiveNumber = 0; foreach (var number in coinsToReceiveNumbers.Skip(1)) { if (number - 1 != previousCoinToReceiveNumber++) { throw new RequestValidationException("Coins to receive numbers should be in a row without gaps", coinsToReceive.Select(x => x.CoinNumber), nameof(coinsToReceive)); } } var coinsToSpendByAssets = coinsToSpend .GroupBy(x => x.Asset) .Select(g => new { Asset = g.Key, Sum = g.Sum(x => x.Value) }) .OrderBy(x => x.Asset) .ToArray(); var coinsToReceiveByAssets = coinsToReceive .GroupBy(x => x.Asset) .Select(g => new { Asset = g.Key, Sum = g.Sum(x => x.Value) }) .OrderBy(x => x.Asset) .ToArray(); var assetsToSpend = coinsToSpendByAssets.Select(x => x.Asset.ToString()).ToArray(); var assetsToReceive = coinsToReceiveByAssets.Select(x => x.Asset.ToString()).ToArray(); if (!assetsToSpend.SequenceEqual(assetsToReceive)) { throw new RequestValidationException( "Sets of coins to spend and coins to receive assets should be equal." + $"{Environment.NewLine}Actual coins to spend assets: [{string.Join(", ", assetsToSpend)}]" + $"{Environment.NewLine}Actual coins to receive assets: [{string.Join(", ", assetsToReceive)}]", new[] { nameof(coinsToSpend), nameof(coinsToReceive) }); } var mismatchedAssetSums = coinsToSpendByAssets .Join( coinsToReceiveByAssets, x => x.Asset, x => x.Asset, (input, output) => new { // ReSharper disable once RedundantAnonymousTypePropertyName Asset = input.Asset, SumToSpend = input.Sum, SumToReceive = output.Sum }) .Where(x => x.SumToSpend < x.SumToReceive) .ToArray(); if (mismatchedAssetSums.Any()) { var mismatchesMessage = new StringBuilder(); mismatchesMessage.AppendLine(); foreach (var assetSum in mismatchedAssetSums) { mismatchesMessage.AppendLine($"Asset: {assetSum.Asset}, sum to spend: {assetSum.SumToSpend}, sum to receive: {assetSum.SumToReceive}"); } throw new RequestValidationException( $"Sum to spend and to receive of each asset should be equal. Mismatched sum: {mismatchesMessage}", new[] { nameof(coinsToSpend), nameof(coinsToReceive) }); } }