@@ -344,10 +344,159 @@ Extended::getLatestInclusion(const std::vector<Type::Trytes>& hashes) const {
344344 return getInclusionStates (hashes, { getNodeInfo ().getLatestSolidSubtangleMilestone () });
345345}
346346
347+ // TODO Response ?
347348std::vector<Type::Trytes>
348- Extended::prepareTransfers (const Type::Trytes&, int , const std::vector<Transfer>&,
349- const std::string&, const std::vector<input>&) const {
350- return {};
349+ Extended::prepareTransfers (const Type::Trytes& seed, int security, std::vector<Transfer>& transfers,
350+ const std::string& remainder, const std::vector<input>& inputs,
351+ bool validateInputs) const {
352+ Utils::StopWatch sw;
353+ // Validate transfers object
354+ if (!this ->isTransfersCollectionValid (transfers)) {
355+ throw Errors::IllegalState (" Invalid transfer" );
356+ }
357+
358+ // Validate the seed
359+ if ((!Type::Seed::isValidSeed (seed))) {
360+ throw Errors::IllegalState (" Invalid Seed" );
361+ }
362+
363+ // Validate the security level
364+ if (security < 1 || security > 3 ) {
365+ throw Errors::IllegalState (" Invalid Security Level" );
366+ }
367+
368+ Bundle bundle;
369+ std::vector<std::string> signatureFragments;
370+ long totalValue = 0 ;
371+ std::string tag;
372+ Crypto::Checksum cs;
373+
374+ // Iterate over all transfers, get totalValue
375+ // and prepare the signatureFragments, message and tag
376+ for (auto & transfer : transfers) {
377+ // If address with checksum then remove checksum
378+ if (cs.isValid (transfer.getAddress ()))
379+ transfer.setAddress (cs.remove (transfer.getAddress ()));
380+
381+ int signatureMessageLength = 1 ;
382+
383+ // If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction)
384+ if (transfer.getMessage ().size () > MaxTrxMsgLength) {
385+ // Get total length, message / maxLength (2187 trytes)
386+ signatureMessageLength += std::floor (transfer.getMessage ().length () / MaxTrxMsgLength);
387+
388+ std::string msgCopy = transfer.getMessage ();
389+
390+ // While there is still a message, copy it
391+ while (!msgCopy.empty ()) {
392+ auto fragment = msgCopy.substr (0 , MaxTrxMsgLength);
393+ msgCopy = msgCopy.substr (MaxTrxMsgLength);
394+
395+ // Pad remainder of fragment
396+ fragment = Type::Utils::rightPad (transfer.getMessage (), MaxTrxMsgLength, ' 9' );
397+
398+ signatureFragments.push_back (fragment);
399+ }
400+ } else {
401+ // Else, get single fragment with 2187 of 9's trytes
402+ auto fragment = transfer.getMessage ().substr (0 , MaxTrxMsgLength);
403+
404+ fragment = Type::Utils::rightPad (fragment, MaxTrxMsgLength, ' 9' );
405+
406+ signatureFragments.push_back (fragment);
407+ }
408+
409+ // get current timestamp in seconds
410+ long timestamp = sw.now ().count ();
411+
412+ // If no tag defined, get 27 tryte tag.
413+ tag = transfer.getTag ().empty () ? " 999999999999999999999999999" : transfer.getTag ();
414+
415+ // Pad for required 27 tryte length
416+ tag = Type::Utils::rightPad (tag, TryteAlphabetLength, ' 9' );
417+
418+ // Add first entry to the bundle
419+ bundle.addTransaction (signatureMessageLength, transfer.getAddress (), transfer.getValue (), tag,
420+ timestamp);
421+ // Sum up total value
422+ totalValue += transfer.getValue ();
423+ }
424+
425+ // Get inputs if we are sending tokens
426+ if (totalValue != 0 ) {
427+ if (!validateInputs)
428+ return this ->addRemainder (seed, security, inputs, bundle, tag, totalValue, remainder,
429+ signatureFragments);
430+ // Case 1: user provided inputs
431+ // Validate the inputs by calling getBalances
432+ if (!validateInputs)
433+ return addRemainder (seed, security, inputs, bundle, tag, totalValue, remainder,
434+ signatureFragments);
435+ if (not inputs.empty ()) {
436+ // Get list if addresses of the provided inputs
437+ std::vector<std::string> inputsAddresses;
438+ for (const auto & input : inputs) {
439+ inputsAddresses.push_back (input.getAddress ());
440+ }
441+
442+ // TODO 100 ?
443+ auto balancesResponse = this ->getBalances (inputsAddresses, 100 );
444+ auto balances = balancesResponse.getBalances ();
445+
446+ std::vector<input> confirmedInputs;
447+ int totalBalance = 0 ;
448+ int i = 0 ;
449+ for (const auto & balance : balances) {
450+ long thisBalance = std::stol (balance);
451+
452+ // If input has balance, add it to confirmedInputs
453+ if (thisBalance > 0 ) {
454+ totalBalance += thisBalance;
455+ auto inputEl = inputs[i++];
456+ inputEl.setBalance (thisBalance);
457+ confirmedInputs.push_back (inputEl);
458+
459+ // if we've already reached the intended input value, break out of loop
460+ if (totalBalance >= totalValue) {
461+ break ;
462+ }
463+ }
464+ }
465+
466+ // Return not enough balance error
467+ if (totalValue > totalBalance) {
468+ throw Errors::IllegalState (" Not enough balance" );
469+ }
470+
471+ return this ->addRemainder (seed, security, confirmedInputs, bundle, tag, totalValue, remainder,
472+ signatureFragments);
473+ }
474+
475+ // Case 2: Get inputs deterministically
476+ //
477+ // If no inputs provided, derive the addresses from the seed and
478+ // confirm that the inputs exceed the threshold
479+ else {
480+ auto newinputs = this ->getInputs (seed, security, 0 , 0 , totalValue);
481+ // If inputs with enough balance
482+ return addRemainder (seed, security, newinputs.getInput (), bundle, tag, totalValue, remainder,
483+ signatureFragments);
484+ }
485+ } else {
486+ // If no input required, don't sign and simply finalize the bundle
487+ auto curl = Crypto::create (this ->cryptoType_ );
488+ bundle.finalize (curl);
489+ bundle.addTrytes (signatureFragments);
490+
491+ auto trxb = bundle.getTransactions ();
492+ std::vector<std::string> bundleTrytes;
493+
494+ for (const auto & trx : trxb) {
495+ bundleTrytes.push_back (trx.toTrytes ());
496+ }
497+ std::reverse (bundleTrytes.begin (), bundleTrytes.end ());
498+ return bundleTrytes;
499+ }
351500}
352501
353502getBundleResponse
@@ -469,7 +618,7 @@ Extended::replayTransfer() const {
469618
470619sendTransferResponse
471620Extended::sendTransfer (const Type::Trytes& seed, int security, int depth, int minWeightMagnitude,
472- const std::vector<Transfer>& transfers, const std::vector<input>& inputs,
621+ std::vector<Transfer>& transfers, const std::vector<input>& inputs,
473622 const Type::Trytes& address) const {
474623 // Validate the security level
475624 if (security < 1 || security > 3 ) {
@@ -578,12 +727,12 @@ Extended::addRemainder(const Type::Trytes& seed, const unsigned int& security,
578727 const std::vector<input>& inputs, Bundle& bundle, const std::string& tag,
579728 const long & totalValue, const Type::Trytes& remainderAddress,
580729 const std::vector<std::string>& signatureFragments) const {
581- auto totalTransferValue = totalValue;
730+ Utils::StopWatch sw;
731+ auto totalTransferValue = totalValue;
582732 for (const auto & input : inputs) {
583733 auto thisBalance = input.getBalance ();
584734 auto toSubtract = -thisBalance;
585- // TODO std::chrono ?
586- auto timestamp = std::time (0 );
735+ long timestamp = sw.now ().count ();
587736 // Add input as bundle entry
588737 bundle.addTransaction (input.getSecurity (), input.getAddress (), toSubtract, tag, timestamp);
589738 // If there is a remainder value
@@ -642,7 +791,8 @@ Extended::replayBundle(const Type::Trytes& transaction, int depth, int minWeight
642791
643792std::vector<Transaction>
644793Extended::initiateTransfer (int securitySum, const std::string& inputAddress,
645- const std::string& remainderAddress, std::vector<Transfer>& transfers) {
794+ const std::string& remainderAddress,
795+ std::vector<Transfer>& transfers) const {
646796 Utils::StopWatch sw;
647797 Crypto::Checksum checksum;
648798
@@ -887,7 +1037,7 @@ Extended::signInputsAndReturn(const std::string& seed, const std::vector<input>&
8871037}
8881038
8891039bool
890- Extended::isTransfersCollectionValid (const std::vector<Transfer>& transfers) {
1040+ Extended::isTransfersCollectionValid (const std::vector<Transfer>& transfers) const {
8911041 for (const auto & transfer : transfers) {
8921042 if (!transfer.isValid ()) {
8931043 return false ;
0 commit comments