Ejemplo n.º 1
0
        public void GeneratedTryteTest()
        {
            var trytes = IotaApiUtils.GenerateRandomTrytes();

            Assert.AreEqual(81, trytes.Length);
            Assert.IsTrue(ContainsChars(trytes, Constants.TryteAlphabet));
            //Assert.IsTrue(ContainsChars(Constants.TryteAlphabet, trytes)); // sometimes can throw
        }
Ejemplo n.º 2
0
        private List <string> AddRemainder(string seed, List <Input> inputs, Bundle bundle, string tag, long totalValue,
                                           string remainderAddress, List <string> signatureFragments)
        {
            long totalTransferValue = totalValue;

            foreach (Input input in inputs)
            {
                var thisBalance = input.Balance;
                var toSubtract  = 0 - thisBalance;
                var timestamp   = IotaApiUtils.CreateTimeStampNow();

                // Add input as bundle entry
                bundle.AddEntry(2, input.Address, toSubtract, tag, timestamp);
                // If there is a remainder value
                // Add extra output to send remaining funds to

                if (thisBalance >= totalTransferValue)
                {
                    var remainder = thisBalance - totalTransferValue;

                    // If user has provided remainder address
                    // Use it to send remaining funds to
                    if (remainder > 0 && remainderAddress != null)
                    {
                        // Remainder bundle entry
                        bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp);

                        // function for signing inputs
                        IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, curl);
                    }
                    else if (remainder > 0)
                    {
                        // Generate a new Address by calling getNewAddress
                        string address = GetNewAddress(seed)[0];

                        // Remainder bundle entry
                        bundle.AddEntry(1, address, remainder, tag, timestamp);

                        // function for signing inputs
                        return(IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, curl));
                    }
                    else
                    {
                        // If there is no remainder, do not add transaction to bundle
                        // simply sign and return
                        return(IotaApiUtils.SignInputsAndReturn(seed, inputs, bundle, signatureFragments, curl));
                    }
                }
                // If multiple inputs provided, subtract the totalTransferValue by
                // the inputs balance
                else
                {
                    totalTransferValue -= thisBalance;
                }
            }

            throw new NotEnoughBalanceException(totalValue);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work.
        /// You need to supply branchTransaction as well as trunkTransaction
        /// (basically the tips which you're going to validate and reference with this transaction)
        ///  - both of which you'll get through the getTransactionsToApprove API call.
        /// </summary>
        /// <param name="trunkTransaction">Trunk transaction to approve.</param>
        /// <param name="branchTransaction">Branch transaction to approve.</param>
        /// <param name="trytes">List of trytes (raw transaction data) to attach to the tangle.</param>
        /// <param name="minWeightMagnitude">Proof of Work intensity. Minimum value is 18</param>
        /// <returns>The returned value contains a different set of tryte values which you can input into broadcastTransactions and storeTransactions.
        /// The returned tryte value, the last 243 trytes basically consist of the: trunkTransaction + branchTransaction + nonce.
        /// These are valid trytes which are then accepted by the network.</returns>
        public AttachToTangleResponse AttachToTangle(string trunkTransaction, string branchTransaction,
                                                     string[] trytes, int minWeightMagnitude = 18)
        {
            if (!InputValidator.IsHash(trunkTransaction))
            {
                throw new ArgumentException("Invalid hashes provided.");
            }

            if (!InputValidator.IsHash(branchTransaction))
            {
                throw new ArgumentException("Invalid hashes provided.");
            }

            if (!InputValidator.IsArrayOfTrytes(trytes, 2673))
            {
                throw new ArgumentException("Invalid trytes provided.");
            }

            if (LocalPow != null)
            {
                var response = new AttachToTangleResponse
                {
                    Trytes = new List <string>()
                };

                string previousTransaction = null;
                foreach (var t in trytes)
                {
                    var txn = new Transaction(t)
                    {
                        TrunkTransaction  = previousTransaction ?? trunkTransaction,
                        BranchTransaction = previousTransaction == null ? branchTransaction : trunkTransaction
                    };

                    if (string.IsNullOrEmpty(txn.Tag) || txn.Tag.Matches("9*"))
                    {
                        txn.Tag = txn.ObsoleteTag;
                    }
                    txn.AttachmentTimestamp           = IotaApiUtils.CreateTimeStampNow();
                    txn.AttachmentTimestampLowerBound = 0;
                    txn.AttachmentTimestampUpperBound = 3_812_798_742_493L;

                    var resultTrytes = LocalPow.PerformPoW(txn.ToTransactionTrytes(), minWeightMagnitude);

                    previousTransaction = new Transaction(resultTrytes).Hash;

                    response.Trytes.Add(resultTrytes);
                }

                return(response);
            }

            AttachToTangleRequest attachToTangleRequest = new AttachToTangleRequest(trunkTransaction, branchTransaction,
                                                                                    trytes, minWeightMagnitude);

            return(_genericIotaCoreApi.Request <AttachToTangleRequest, AttachToTangleResponse>(attachToTangleRequest));
        }
Ejemplo n.º 4
0
        public async Task <string> ApproveTransaction(string transactionHash, ApproveTransactionType approveType)
        {
            var api = CreateIotaClient();
            //var address = await api.GetAddress(TestSeed2, 8);

            var emptyAddress = IotaApiUtils.GenerateRandomTrytes(81); // "".Pad(81);

            var transfer = new TransferItem()
            {
                Address = emptyAddress,
                Value   = 0,
                Message = "",
                Tag     = ""
            };

            while (true)
            {
                try
                {
                    CancellationTokenSource cts = new CancellationTokenSource();

                    var transactions = transfer.CreateTransactions();
                    var trytesToPow  = transactions.GetTrytes().Single();
                    var toApprove    = await api.IriApi.GetTransactionsToApprove(9);

                    var diver = new PowDiver();
                    cts.CancelAfter(20000);
                    var trunk  = toApprove.TrunkTransaction;
                    var branch = toApprove.BranchTransaction;

                    if (approveType == ApproveTransactionType.Trunk)
                    {
                        trunk = transactionHash;
                    }
                    else
                    {
                        branch = transactionHash;
                    }

                    var trytesToSend = await diver.DoPow(trytesToPow.SetApproveTransactions(trunk, branch), 15, cts.Token);

                    await api.IriApi.BroadcastTransactions(trytesToSend);

                    await api.IriApi.StoreTransactions(trytesToSend);

                    var transaction = new TransactionItem(trytesToSend);

                    return(transaction.Hash);
                }
                catch (OperationCanceledException)
                {
                    continue;
                }
            }
        }
Ejemplo n.º 5
0
        public void TestAddressGeneration()
        {
            Assert.AreEqual(FIRST_ADDR, IotaApiUtils.NewAddress(TEST_SEED, 2, 0, true, null));
            Assert.AreEqual(SIXTH_ADDR, IotaApiUtils.NewAddress(TEST_SEED, 2, 5, true, null));

            Assert.AreEqual(ADDR_I0_S1, IotaApiUtils.NewAddress(ADDR_SEED, 1, 0, false, null));
            Assert.AreEqual(ADDR_I0_S2, IotaApiUtils.NewAddress(ADDR_SEED, 2, 0, false, null));
            Assert.AreEqual(ADDR_I0_S3, IotaApiUtils.NewAddress(ADDR_SEED, 3, 0, false, null));

            Assert.AreEqual(ADDR_LS_I0_S1, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 1, 0, false, null));
            Assert.AreEqual(ADDR_LS_I0_S2, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 2, 0, false, null));
            Assert.AreEqual(ADDR_LS_I0_S3, IotaApiUtils.NewAddress(ADDR_SEED + ADDR_SEED, 3, 0, false, null));
        }
Ejemplo n.º 6
0
        /// <summary>
        ///     Gets all possible inputs of a seed and returns them with the total balance.
        ///     This is either done deterministically (by genearating all addresses until findTransactions is empty and doing
        ///     getBalances),
        ///     or by providing a key range to use for searching through.
        /// </summary>
        /// <param name="seed">Tryte-encoded seed. It should be noted that this seed is not transferred</param>
        /// <param name="security">The Security level of private key / seed.</param>
        /// <param name="start">Starting key index</param>
        /// <param name="end">Ending key index</param>
        /// <param name="threshold">The minimum threshold of accumulated balances from the inputs that is required</param>
        /// <returns>The inputs (see <see cref="Input" />) </returns>
        public Inputs GetInputs(string seed, int security, int start, int end, long threshold)
        {
            InputValidator.CheckIfValidSeed(seed);

            seed = InputValidator.PadSeedIfNecessary(seed);

            if (security < 1)
            {
                throw new ArgumentException("invalid security level provided");
            }

            // If start value bigger than end, return error
            if (start > end)
            {
                throw new ArgumentException("start must be smaller than end", nameof(start));
            }

            // or if difference between end and start is bigger than 500 keys
            if (end - start > 500)
            {
                throw new ArgumentException("total number of keys exceeded 500");
            }

            //  Case 1: start and end
            //
            //  If start and end is defined by the user, simply iterate through the keys
            //  and call getBalances
            if (end != 0)
            {
                var allAddresses = new string[end - start];

                for (var i = start; i < end; i++)
                {
                    var address = IotaApiUtils.NewAddress(seed, security, i, false, _curl);
                    allAddresses[i] = address;
                }

                return(GetBalanceAndFormat(allAddresses, threshold, start, security));
            }

            //  Case 2: iterate till threshold || end
            //
            //  Either start from index: 0 or start (if defined) until threshold is reached.
            //  Calls getNewAddress and deterministically generates and returns all addresses
            //  We then do getBalance, format the output and return it

            var addresses = GetNewAddress(seed, security, start, false, 0, true);

            return(GetBalanceAndFormat(addresses, threshold, start, security));
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Generates a new address from a seed and returns the remainderAddress. This is either done deterministically, or by providing the index of the new remainderAddress
        /// </summary>
        /// <param name="seed">Tryte-encoded seed. It should be noted that this seed is not transferred</param>
        /// <param name="index">Optional (default null). Key index to start search from. If the index is provided, the generation of the address is not deterministic.</param>
        /// <param name="checksum">Optional (default false). Adds 9-tryte address checksum</param>
        /// <param name="total">Optional (default 1)Total number of addresses to generate.</param>
        /// <param name="returnAll">If true, it returns all addresses which were deterministically generated (until findTransactions returns null)</param>
        /// <returns>an array of strings with the specifed number of addresses</returns>
        public string[] GetNewAddress(string seed, int index = 0, bool checksum = false, int total = 0,
                                      bool returnAll         = false)
        {
            List <string> allAdresses = new List <string>();

            // TODO make two different functions out of this

            // Case 1: total
            //
            // If total number of addresses to generate is supplied, simply generate
            // and return the list of all addresses
            if (total > 0)
            {
                // Increase index with each iteration
                for (int i = index; i < index + total; i++)
                {
                    allAdresses.Add(IotaApiUtils.NewAddress(seed, i, checksum, curl));
                }

                return(allAdresses.ToArray());
            }

            //  Case 2: no total provided
            //
            //  Continue calling findTransactions to see if address was already created
            //  if null, return list of addresses
            //
            else
            {
                List <string> addresses = new List <string>();

                for (int i = index;; i++)
                {
                    string newAddress = IotaApiUtils.NewAddress(seed, i, checksum, curl);
                    FindTransactionsResponse response = FindTransactionsByAddresses(newAddress);

                    if (returnAll)
                    {
                        addresses.Add(newAddress);
                    }

                    if (response.Hashes.Count == 0)
                    {
                        break;
                    }
                }

                return(addresses.ToArray());
            }
        }
Ejemplo n.º 8
0
        private string GetFirstUnusedAddress(string seed, int securityLevel, int index, bool checksum)
        {
            while (true)
            {
                string newAddress =
                    IotaApiUtils.NewAddress(
                        seed, securityLevel, index, checksum,
                        SpongeFactory.Create(SpongeFactory.Mode.KERL));
                if (!IsAddressSpent(newAddress, checksum))
                {
                    return(newAddress);
                }

                index++;
            }
        }
Ejemplo n.º 9
0
        public async Task SendVeryEmptyTransactionTest()
        {
            var api = CreateIotaClient();

            var emptyAddress = IotaApiUtils.GenerateRandomTrytes(81); // "".Pad(81);

            var transfer = new TransferItem()
            {
                Address = emptyAddress,
                Value   = 0,
                Message = null,
                Tag     = null
            };

            while (true)
            {
                try
                {
                    CancellationTokenSource cts = new CancellationTokenSource();

                    var transactions = transfer.CreateTransactions();
                    var trytesToPow  = transactions.GetTrytes().Single();
                    var toApprove    = await api.IriApi.GetTransactionsToApprove(9);

                    var diver = new PowDiver();
                    //cts.CancelAfter(15000);
                    var trunk  = toApprove.TrunkTransaction;
                    var branch = toApprove.BranchTransaction;


                    var trytesToSend = await diver.DoPow(trytesToPow.SetApproveTransactions(trunk, branch), 15, cts.Token);

                    await api.IriApi.BroadcastTransactions(trytesToSend);

                    await api.IriApi.StoreTransactions(trytesToSend);

                    var transaction = new TransactionItem(trytesToSend);
                }
                catch (OperationCanceledException)
                {
                    continue;
                }
            }
        }
Ejemplo n.º 10
0
        private async Task <AddressItem> NewAddressAsync(string seed, int index, CancellationToken cancellationToken)
        {
            await TaskIota.Yield().ConfigureAwait(false);

            // need yield because new address generation is very costly

            int[]  key     = new Signing(new Curl()).Key(Converter.ToTrits(seed), index, 2);
            string address = IotaApiUtils.NewAddress(key, false, new Curl(), cancellationToken);

            var addressItem = new AddressItem()
            {
                Address    = address,
                PrivateKey = key,
                Index      = index,
                Balance    = 0
            };

            await RenewTransactions(addressItem);

            return(addressItem);
        }
Ejemplo n.º 11
0
        private static IEnumerable <TransactionItem> CreateDepositTransaction(TransferItem transferItem)
        {
            var timestamp = IotaApiUtils.CreateTimeStampNow().ToString();
            var tag       = transferItem.Tag.ValidateTrytes(nameof(transferItem.Tag)).Pad(27);

            var messages = ChunksUpto(transferItem.Message, 2187).ToArray();

            for (int i = 0; i < messages.Length; i++)
            {
                var message = messages[i].ValidateTrytes(nameof(transferItem.Message)).Pad(2187);

                var transactionItem = new TransactionItem()
                {
                    Address           = transferItem.Address,
                    SignatureFragment = message,
                    Value             = (i == 0 ? transferItem.Value : 0).ToString(),
                    Timestamp         = timestamp,
                    Tag = tag,
                };

                yield return(transactionItem);
            }
        }
Ejemplo n.º 12
0
        private List <string> GetAddresses(string seed, int securityLevel, int index, bool checksum, int amount,
                                           bool addSpendAddresses)
        {
            List <string> addresses = new List <string>();

            for (int i = index, numUnspentFound = 0; numUnspentFound < amount; i++)
            {
                string newAddress = IotaApiUtils.NewAddress(seed, securityLevel, i, checksum,
                                                            SpongeFactory.Create(SpongeFactory.Mode.KERL));

                if (!IsAddressSpent(newAddress, checksum))
                {
                    addresses.Add(newAddress);
                    numUnspentFound++;
                }
                else if (addSpendAddresses)
                {
                    addresses.Add(newAddress);
                }
            }

            return(addresses);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// This does not mean that these addresses are safe to use (unspent)
        /// </summary>
        /// <param name="addressRequest"></param>
        /// <returns></returns>
        public GetNewAddressResponse GetAddressesUnchecked(AddressRequest addressRequest)
        {
            var stopwatch = Stopwatch.StartNew();

            List <string> addresses = new List <string>();

            for (int i = 0; i < addressRequest.Amount; i++)
            {
                addresses.Add(
                    IotaApiUtils.NewAddress(
                        addressRequest.Seed,
                        addressRequest.SecurityLevel, i,
                        addressRequest.Checksum,
                        SpongeFactory.Create(SpongeFactory.Mode.KERL)));
            }

            stopwatch.Stop();

            return(new GetNewAddressResponse
            {
                Addresses = addresses,
                Duration = stopwatch.ElapsedMilliseconds
            });
        }
Ejemplo n.º 14
0
        /// <summary>
        ///     Main purpose of this function is to get an array of transfer objects as input, and then prepare the transfer by
        ///     generating the correct bundle,
        ///     as well as choosing and signing the inputs if necessary (if it's a value transfer). The output of this function is
        ///     an array of the raw transaction data (trytes)
        /// </summary>
        /// <param name="seed">81-tryte encoded address of recipient</param>
        /// <param name="security"></param>
        /// <param name="transfers">the transfers to prepare</param>
        /// <param name="inputs">Optional (default null). The inputs</param>
        /// <param name="remainderAddress">
        ///     Optional (default null). if defined, this address will be used for sending the remainder
        ///     value (of the inputs) to.
        /// </param>
        /// <param name="validateInputs"></param>
        /// <returns>a list containing the trytes of the new bundle</returns>
        public List <string> PrepareTransfers(
            string seed, int security,
            Transfer[] transfers,
            string remainderAddress,
            List <Input> inputs,
            bool validateInputs)
        {
            // validate seed
            if (!InputValidator.IsValidSeed(seed))
            {
                throw new IllegalStateException("Invalid seed provided.");
            }


            if (security < 1)
            {
                throw new ArgumentException("Invalid security level provided.");
            }

            // Input validation of transfers object
            InputValidator.CheckTransferArray(transfers);

            // Create a new bundle
            var bundle             = new Bundle();
            var signatureFragments = new List <string>();

            long totalValue = 0;
            var  tag        = "";

            //
            //  Iterate over all transfers, get totalValue
            //  and prepare the signatureFragments, message and tag
            //
            foreach (var transfer in transfers)
            {
                // remove the checksum of the address if provided
                transfer.Address = transfer.Address.RemoveChecksum();

                var signatureMessageLength = 1;

                // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction)
                if (transfer.Message.Length > Constants.MessageLength)
                {
                    // Get total length, message / maxLength (2187 trytes)
                    signatureMessageLength += (int)Math.Floor((double)transfer.Message.Length / Constants.MessageLength);

                    var msgCopy = transfer.Message;

                    // While there is still a message, copy it
                    while (!string.IsNullOrEmpty(msgCopy))
                    {
                        var fragment = msgCopy.Substring(0, 2187 > msgCopy.Length ? msgCopy.Length : 2187);
                        msgCopy = msgCopy.Substring(2187, msgCopy.Length - 2187);

                        // Pad remainder of fragment
                        while (fragment.Length < 2187)
                        {
                            fragment += '9';
                        }

                        signatureFragments.Add(fragment);
                    }
                }
                else
                {
                    // Else, get single fragment with 2187 of 9's trytes
                    var fragment = string.Empty;

                    if (!string.IsNullOrEmpty(transfer.Message))
                    {
                        fragment = transfer.Message.Substring(0,
                                                              transfer.Message.Length < 2187 ? transfer.Message.Length : 2187);
                    }

                    while (fragment.Length < 2187)
                    {
                        fragment += '9';
                    }

                    signatureFragments.Add(fragment);
                }

                // get current timestamp in seconds
                var timestamp = (long)Math.Floor((double)IotaApiUtils.CreateTimeStampNow() / 1000);

                // If no tag defined, get 27 tryte tag.

                tag = string.IsNullOrEmpty(transfer.Tag) ? "999999999999999999999999999" : transfer.Tag;


                // Pad for required 27 tryte length
                while (tag.Length < 27)
                {
                    tag += '9';
                }


                // Add first entries to the bundle
                // Slice the address in case the user provided a checksummed one
                bundle.AddEntry(signatureMessageLength, transfer.Address, transfer.Value, tag, timestamp);
                // Sum up total value
                totalValue += transfer.Value;
            }

            // Get inputs if we are sending tokens
            if (totalValue != 0)
            {
                if (inputs != null && inputs.Count > 0)
                {
                    // Get list if addresses of the provided inputs
                    var inputAddresses = new List <string>();
                    foreach (var input in inputs)
                    {
                        inputAddresses.Add(input.Address);
                    }

                    var balances = GetBalances(inputAddresses, 100);

                    var confirmedInputs = new List <Input>();

                    long totalBalance = 0;
                    for (var i = 0; i < balances.Balances.Count; i++)
                    {
                        var thisBalance = balances.Balances[i];
                        totalBalance += thisBalance;

                        // If input has balance, add it to confirmedInputs
                        if (thisBalance > 0)
                        {
                            var inputEl = inputs[i];
                            inputEl.Balance = thisBalance;

                            confirmedInputs.Add(inputEl);
                        }
                    }

                    // Return not enough balance error
                    if (totalValue > totalBalance)
                    {
                        throw new NotEnoughBalanceException(totalValue);
                    }

                    return(AddRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainderAddress,
                                        signatureFragments));
                }

                //  Case 2: Get inputs deterministically
                //
                //  If no inputs provided, derive the addresses from the seed and
                //  confirm that the inputs exceed the threshold
                else
                {
                    var inputList = GetInputs(seed, security, 0, 0, (int)totalValue).InputsList;
                    return(AddRemainder(seed, security, inputList, bundle, tag, totalValue, remainderAddress,
                                        signatureFragments));
                }
            }

            // If no input required, don't sign and simply finalize the bundle
            bundle.FinalizeBundle(_curl.Clone());
            bundle.AddTrytes(signatureFragments);

            var bundleTrytes = new List <string>();

            bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTransactionTrytes()));

            bundleTrytes.Reverse();
            return(bundleTrytes);
        }
Ejemplo n.º 15
0
        private static IEnumerable <TransactionItem> CreateWithdrawalTransactions(string tag, long withdrawAmount, string reminderAddress, params AddressItem[] addressItems)
        {
            if (string.IsNullOrWhiteSpace(reminderAddress))
            {
                throw new ArgumentNullException(nameof(reminderAddress));
            }

            var curl = new Curl();

            tag = tag.ValidateTrytes(nameof(tag)).Pad(27);

            foreach (var addressItem in addressItems)
            {
                if (addressItem.Balance <= 0)
                {
                    continue;
                }

                var timestamp = IotaApiUtils.CreateTimeStampNow().ToString();

                var amount = addressItem.Balance;
                withdrawAmount -= amount;

                var transactionItem = new TransactionItem()
                {
                    Address   = addressItem.Address,
                    Value     = (-amount).ToString(), // withdraw all amount
                    Timestamp = timestamp,
                    Tag       = tag
                };
                yield return(transactionItem);

                transactionItem = new TransactionItem()
                {
                    Address   = addressItem.Address,
                    Value     = "0",
                    Timestamp = timestamp,
                    Tag       = tag
                };
                yield return(transactionItem);



                if (withdrawAmount < 0) // deposit remind amount to reminder address
                {
                    var message = "".Pad(2187);

                    transactionItem = new TransactionItem()
                    {
                        Address           = reminderAddress,
                        Value             = Math.Abs(withdrawAmount).ToString(),
                        Timestamp         = timestamp,
                        SignatureFragment = message,
                        Tag = tag
                    };
                    yield return(transactionItem);
                }

                if (withdrawAmount <= 0)
                {
                    break;
                }
            }
        }
Ejemplo n.º 16
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="seed"></param>
        /// <param name="security"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="threshold"></param>
        /// <param name="tips"></param>
        /// <returns></returns>
        public GetBalancesAndFormatResponse GetInputs(string seed, int security, int start, int end, long threshold,
                                                      params string[] tips)
        {
            // validate the seed
            if (!InputValidator.IsValidSeed(seed))
            {
                throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR);
            }

            seed = InputValidator.PadSeedIfNecessary(seed);

            if (!InputValidator.IsValidSecurityLevel(security))
            {
                throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR);
            }

            // If start value bigger than end, return error
            if (start > end)
            {
                throw new ArgumentException("start must be smaller than end", nameof(start));
            }

            // or if difference between end and start is bigger than 500 keys
            if (end - start > 500)
            {
                throw new ArgumentException("total number of keys exceeded 500");
            }


            Stopwatch stopwatch = Stopwatch.StartNew();

            //  Case 1: start and end
            //
            //  If start and end is defined by the user, simply iterate through the keys
            //  and call getBalances
            if (end != 0)
            {
                var allAddresses = new string[end - start];

                for (var i = start; i < end; i++)
                {
                    var address = IotaApiUtils.NewAddress(seed, security, i, true,
                                                          SpongeFactory.Create(SpongeFactory.Mode.KERL));
                    allAddresses[i] = address;
                }

                return(GetBalanceAndFormat(allAddresses, tips, threshold, start, security, stopwatch));
            }

            {
                //  Case 2: iterate till threshold
                //
                //  Either start from index: 0 or start (if defined) until threshold is reached.
                List <Input> allInputs = new List <Input>();

                bool thresholdReached = true;
                long currentTotal     = 0;

                for (int i = start; thresholdReached; i++)
                {
                    string address = IotaApiUtils.NewAddress(seed, security, i, true,
                                                             SpongeFactory.Create(SpongeFactory.Mode.KERL));

                    // Received input, this epoch or previous
                    GetBalancesResponse response =
                        IotaClient.GetBalances(100, new List <string>()
                    {
                        address
                    }, tips.ToList());
                    var balance = response.Balances[0];

                    if (balance > 0)
                    {
                        // Is it already spent from?
                        WereAddressesSpentFromResponse wasSpent = IotaClient.WereAddressesSpentFrom(address);
                        if (wasSpent.States.Length > 0 && !wasSpent.States[0])
                        {
                            // We can use this!
                            allInputs.Add(new Input
                            {
                                Address = address, Balance = balance, KeyIndex = i, Security = security
                            });
                            currentTotal += balance;

                            if (threshold != 0 && threshold <= currentTotal)
                            {
                                // Stop because we found threshold
                                thresholdReached = false;
                            }
                        }
                    }
                    else
                    {
                        // Check if there was any activity at all
                        FindTransactionsResponse tx = IotaClient.FindTransactionsByAddresses(address);
                        if (tx.Hashes.Count == 0 || i - start > 500)
                        {
                            // Stop because we reached our limit or no activity
                            thresholdReached = false;
                        }
                    }
                }

                stopwatch.Stop();
                return(new GetBalancesAndFormatResponse(allInputs, currentTotal, stopwatch.ElapsedMilliseconds));
            }
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Main purpose of this function is to get an array of transfer objects as input, and then prepare the transfer by generating the correct bundle,
        /// as well as choosing and signing the inputs if necessary (if it's a value transfer). The output of this function is an array of the raw transaction data (trytes)
        /// </summary>
        /// <param name="seed">81-tryte encoded address of recipient</param>
        /// <param name="transfers">the transfers to prepare</param>
        /// <param name="inputs">Optional (default null). The inputs</param>
        /// <param name="remainderAddress">Optional (default null). if defined, this address will be used for sending the remainder value (of the inputs) to.</param>
        /// <returns>a list containing the trytes of the new bundle</returns>
        public List <string> PrepareTransfers(string seed, Transfer[] transfers, List <Input> inputs = null,
                                              string remainderAddress = null)
        {
            InputValidator.CheckTransferArray(transfers);

            // Create a new bundle
            Bundle        bundle             = new Bundle();
            List <string> signatureFragments = new List <string>();
            long          totalValue         = 0;
            string        tag = "";

            //
            //  Iterate over all transfers, get totalValue
            //  and prepare the signatureFragments, message and tag
            //
            for (int i = 0; i < transfers.Length; i++)
            {
                int signatureMessageLength = 1;

                // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction)
                if (transfers[i].Message.Length > 2187)
                {
                    // Get total length, message / maxLength (2187 trytes)
                    signatureMessageLength += (int)Math.Floor(((double)transfers[i].Message.Length / 2187));

                    var msgCopy = transfers[i].Message;

                    // While there is still a message, copy it
                    while (msgCopy != null)
                    {
                        var fragment = msgCopy.Substring(0, 2187 > msgCopy.Length ? msgCopy.Length : 2187);
                        msgCopy = msgCopy.Substring(2187, msgCopy.Length - 2187);

                        // Pad remainder of fragment
                        for (var j = 0; fragment.Length < 2187; j++)
                        {
                            fragment += '9';
                        }

                        signatureFragments.Add(fragment);
                    }
                }
                else
                {
                    // Else, get single fragment with 2187 of 9's trytes
                    string fragment = "";

                    if (transfers[i].Message != null)
                    {
                        fragment = transfers[i].Message.Substring(0,
                                                                  transfers[i].Message.Length < 2187 ? transfers[i].Message.Length : 2187);
                    }

                    for (var j = 0; fragment.Length < 2187; j++)
                    {
                        fragment += '9';
                    }

                    signatureFragments.Add(fragment);
                }

                // get current timestamp in seconds
                long timestamp = IotaApiUtils.CreateTimeStampNow();

                // If no tag defined, get 27 tryte tag.
                tag = transfers[i].Tag != null ? transfers[i].Tag : "999999999999999999999999999";

                // Pad for required 27 tryte length
                for (var j = 0; tag.Length < 27; j++)
                {
                    tag += '9';
                }

                // Add first entries to the bundle
                // Slice the address in case the user provided a checksummed one
                bundle.AddEntry(signatureMessageLength, transfers[i].Address, transfers[i].Value, tag, timestamp);
                // Sum up total value
                totalValue += transfers[i].Value;
            }

            // Get inputs if we are sending tokens
            if (totalValue != 0)
            {
                //  Case 1: user provided inputs
                //
                //  Validate the inputs by calling getBalances
                if (inputs != null && inputs.Count > 0)
                {
                    // Get list if addresses of the provided inputs
                    List <string> inputAddresses = new List <string>();
                    foreach (var input in inputs)
                    {
                        inputAddresses.Add(input.Address);
                    }

                    GetBalancesResponse balances = GetBalances(inputAddresses, 100);

                    List <Input> confirmedInputs = new List <Input>();

                    long totalBalance = 0;
                    for (var i = 0; i < balances.Balances.Count; i++)
                    {
                        var thisBalance = balances.Balances[i];
                        totalBalance += thisBalance;

                        // If input has balance, add it to confirmedInputs
                        if (thisBalance > 0)
                        {
                            var inputEl = inputs[i];
                            inputEl.Balance = thisBalance;

                            confirmedInputs.Add(inputEl);
                        }
                    }

                    // Return not enough balance error
                    if (totalValue > totalBalance)
                    {
                        throw new NotEnoughBalanceException(totalValue);
                    }

                    return(AddRemainder(seed, confirmedInputs, bundle, tag, totalValue, remainderAddress,
                                        signatureFragments));
                }

                //  Case 2: Get inputs deterministically
                //
                //  If no inputs provided, derive the addresses from the seed and
                //  confirm that the inputs exceed the threshold
                else
                {
                    List <Input> inputList = GetInputs(seed, 0, 0, (int)totalValue).InputsList;
                    return(AddRemainder(seed, inputList, bundle, tag, totalValue, remainderAddress, signatureFragments));
                }
            }
            else
            {
                // If no input required, don't sign and simply finalize the bundle
                bundle.FinalizeBundle(curl);
                bundle.AddTrytes(signatureFragments);

                List <String> bundleTrytes = new List <string>();
                bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTransactionTrytes()));

                bundleTrytes.Reverse();
                return(bundleTrytes);
            }
        }
Ejemplo n.º 18
0
        private List <string> AddRemainder(
            string seed,
            int security,
            List <Input> inputs,
            Bundle bundle,
            string tag,
            long totalValue,
            string remainderAddress,
            List <string> signatureFragments)
        {
            var totalTransferValue = totalValue;

            foreach (var input in inputs)
            {
                var thisBalance = input.Balance;
                var toSubtract  = 0 - thisBalance;
                var timestamp   = TimeStamp.Now();

                // Add input as bundle entry
                // use input.Security
                bundle.AddEntry(input.Security, input.Address, toSubtract, tag, timestamp);
                // If there is a remainder value
                // Add extra output to send remaining funds to

                if (thisBalance >= totalTransferValue)
                {
                    var remainder = thisBalance - totalTransferValue;

                    // If user has provided remainder address
                    // Use it to send remaining funds to
                    if (remainder > 0 && remainderAddress != null)
                    {
                        // Remainder bundle entry
                        bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp);

                        // function for signing inputs
                        return(IotaApiUtils.SignInputsAndReturn(
                                   seed, inputs, bundle, signatureFragments,
                                   SpongeFactory.Create(SpongeFactory.Mode.KERL)));
                    }

                    if (remainder > 0)
                    {
                        AddressRequest addressRequest = new AddressRequest(seed, security);
                        var            res            = GenerateNewAddresses(addressRequest);

                        // Remainder bundle entry
                        bundle.AddEntry(1, res.Addresses[0], remainder, tag, timestamp);

                        // function for signing inputs
                        return(IotaApiUtils.SignInputsAndReturn(
                                   seed, inputs, bundle, signatureFragments,
                                   SpongeFactory.Create(SpongeFactory.Mode.KERL)));
                    }

                    // If there is no remainder, do not add transaction to bundle
                    // simply sign and return
                    return(IotaApiUtils.SignInputsAndReturn(
                               seed, inputs, bundle, signatureFragments,
                               SpongeFactory.Create(SpongeFactory.Mode.KERL)));
                }

                // If multiple inputs provided, subtract the totalTransferValue by
                // the inputs balance
                totalTransferValue -= thisBalance;
            }

            throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR);
        }