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> /// 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 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) }); } }