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); }
/// <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> /// /// </summary> /// <param name="securitySum"></param> /// <param name="inputAddress"></param> /// <param name="remainderAddress"></param> /// <param name="transfers"></param> /// <param name="testMode"></param> /// <returns></returns> public List <Transaction> InitiateTransfer( int securitySum, string inputAddress, string remainderAddress, List <Transfer> transfers, bool testMode) { // validate input address if (!InputValidator.IsAddress(inputAddress)) { throw new ArgumentException("Invalid addresses provided."); } // validate remainder address if (remainderAddress != null && !InputValidator.IsAddress(remainderAddress)) { throw new ArgumentException("Invalid addresses provided."); } // Input validation of transfers object if (!InputValidator.IsTransfersCollectionValid(transfers)) { throw new ArgumentException("Invalid transfers provided."); } // Create a new bundle Bundle bundle = new Bundle(); long totalValue = 0; List <String> signatureFragments = new List <string>(); String tag = ""; // // Iterate over all transfers, get totalValue // and prepare the signatureFragments, message and tag foreach (Transfer transfer in transfers) { // remove the checksum of the address if provided if (transfer.Address.IsValidChecksum()) { transfer.Address = transfer.Address.RemoveChecksum(); } int signatureMessageLength = 1; // If message longer than 2187 trytes, increase signatureMessageLength (add next transaction) if (transfer.Message.Length > Constants.MessageLength) { // Get total length, message / maxLength (2187 trytes) signatureMessageLength += (int)Math.Floor((double)transfer.Message.Length / Constants.MessageLength); String msgCopy = transfer.Message; // While there is still a message, copy it while (!string.IsNullOrEmpty(msgCopy)) { string fragment = msgCopy.Substring(0, Constants.MessageLength); msgCopy = msgCopy.Substring(Constants.MessageLength, msgCopy.Length - Constants.MessageLength); // Pad remainder of fragment fragment = fragment.PadRight(Constants.MessageLength, '9'); signatureFragments.Add(fragment); } } else { // Else, get single fragment with 2187 of 9's trytes String fragment = transfer.Message; if (transfer.Message.Length < Constants.MessageLength) { fragment = fragment.PadRight(Constants.MessageLength, '9'); } signatureFragments.Add(fragment); } tag = transfer.Tag; // pad for required 27 tryte length if (transfer.Tag.Length < Constants.TagLength) { tag = tag.PadRight(Constants.TagLength, '9'); } // get current timestamp in seconds long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); // Add first entry to the bundle 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) { GetBalancesResponse balancesResponse = GetBalances(new List <string> { inputAddress }, 100); var balances = balancesResponse.Balances; long totalBalance = 0; foreach (var balance in balances) { totalBalance += balance; } // get current timestamp in seconds long timestamp = (long)Math.Floor(GetCurrentTimestampInSeconds()); // bypass the balance checks during unit testing if (testMode) { totalBalance += 1000; } if (totalBalance > 0) { long toSubtract = 0 - totalBalance; // Add input as bundle entry // Only a single entry, signatures will be added later bundle.AddEntry(securitySum, inputAddress, toSubtract, tag, timestamp); } // Return not enough balance error if (totalValue > totalBalance) { throw new IllegalStateException("Not enough balance."); } // If there is a remainder value // Add extra output to send remaining funds to if (totalBalance > totalValue) { long remainder = totalBalance - totalValue; // Remainder bundle entry if necessary if (remainderAddress == null) { throw new IllegalStateException("No remainder address defined."); } bundle.AddEntry(1, remainderAddress, remainder, tag, timestamp); } bundle.FinalizeBundle(new Curl(CurlMode.CurlP81)); bundle.AddTrytes(signatureFragments); return(bundle.Transactions); } else { throw new System.Exception("Invalid value transfer: the transfer does not require a signature."); } }
/// <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); } }
/// <summary> /// /// </summary> /// <param name="seed"></param> /// <param name="security"></param> /// <param name="transfers"></param> /// <param name="remainder"></param> /// <param name="inputs"></param> /// <param name="tips"></param> /// <param name="validateInputs"></param> /// <returns></returns> public List <string> PrepareTransfers( string seed, int security, Transfer[] transfers, string remainder, Input[] inputs, Transaction[] tips, bool validateInputs) { // validate seed if (!InputValidator.IsValidSeed(seed)) { throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); } if (!InputValidator.IsValidSecurityLevel(security)) { throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); } if (remainder != null && !InputValidator.IsAddress(remainder)) { throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); } // Input validation of transfers object if (!InputValidator.IsValidTransfersCollection(transfers.ToList())) { throw new ArgumentException(Constants.INVALID_TRANSFERS_INPUT_ERROR); } if (inputs != null && !InputValidator.IsValidInputsCollection(inputs)) { throw new ArgumentException(Constants.INVALID_ADDRESSES_INPUT_ERROR); } // 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.MESSAGE_LENGTH) { // Get total length, message / maxLength (2187 trytes) signatureMessageLength += (int)Math.Floor((double)transfer.Message.Length / Constants.MESSAGE_LENGTH); 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(fragment.Length, msgCopy.Length - fragment.Length); // Pad remainder of fragment if (fragment.Length < 2187) { fragment = fragment.PadRight(2187, '9'); } signatureFragments.Add(fragment); } } else { // Else, get single fragment with 2187 of 9's trytes var fragment = transfer.Message.PadRight(Constants.MESSAGE_LENGTH, '9'); signatureFragments.Add(fragment); } // get current timestamp in seconds var timestamp = (long)Math.Floor((double)TimeStamp.Now() / 1000); // If no tag defined, get 27 tryte tag. tag = string.IsNullOrEmpty(transfer.Tag) ? "999999999999999999999999999" : transfer.Tag; // Pad for required 27 tryte length if (tag.Length < Constants.TAG_LENGTH) { tag = tag.PadRight(Constants.TAG_LENGTH, '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) { // validate seed if (!InputValidator.IsValidSeed(seed)) { throw new IllegalStateException(Constants.INVALID_SEED_INPUT_ERROR); } // Case 1: user provided inputs // Validate the inputs by calling getBalances if (inputs != null && inputs.Length > 0) { if (!validateInputs) { return(AddRemainder(seed, security, inputs.ToList(), bundle, tag, totalValue, remainder, signatureFragments)); } // Get list if addresses of the provided inputs var inputAddresses = new List <string>(); foreach (var input in inputs) { inputAddresses.Add(input.Address); } List <string> tipHashes = null; if (tips != null) { tipHashes = new List <string>(); foreach (var tx in tips) { tipHashes.Add(tx.CurlHash()); } } var balances = IotaClient.GetBalances(100, inputAddresses, tipHashes); 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); // if we've already reached the intended input value, break out of loop if (totalBalance >= totalValue) { Log.Info("Total balance already reached "); break; } } } // Return not enough balance error if (totalValue > totalBalance) { throw new IllegalStateException(Constants.NOT_ENOUGH_BALANCE_ERROR); } return(AddRemainder(seed, security, confirmedInputs, bundle, tag, totalValue, remainder, signatureFragments)); } // Case 2: Get inputs deterministically // // If no inputs provided, derive the addresses from the seed and // confirm that the inputs exceed the threshold var inputList = GetInputs(seed, security, 0, 0, (int)totalValue).Inputs; return(AddRemainder(seed, security, inputList, bundle, tag, totalValue, remainder, signatureFragments)); } // If no input required, don't sign and simply finalize the bundle bundle.FinalizeBundle(SpongeFactory.Create(SpongeFactory.Mode.KERL)); bundle.AddTrytes(signatureFragments); var bundleTrytes = new List <string>(); bundle.Transactions.ForEach(tx => bundleTrytes.Add(tx.ToTrytes())); bundleTrytes.Reverse(); return(bundleTrytes); }
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); }