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