Example #1
0
        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));
            }
        }
Example #2
0
        /// <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) });
            }
        }