|
32 | 32 | #include <Model/Signature.hpp> |
33 | 33 | #include <Model/Transaction.hpp> |
34 | 34 | #include <Type/Seed.hpp> |
| 35 | +#include <Type/utils.hpp> |
35 | 36 |
|
36 | 37 | namespace IOTA { |
37 | 38 |
|
@@ -639,6 +640,142 @@ Extended::replayBundle(const Type::Trytes& transaction, int depth, int minWeight |
639 | 640 | return { successful, stopWatch.getElapsedTimeMiliSeconds().count() }; |
640 | 641 | } |
641 | 642 |
|
| 643 | +std::vector<Transaction> |
| 644 | +Extended::initiateTransfer(int securitySum, const std::string& inputAddress, |
| 645 | + const std::string& remainderAddress, std::vector<Transfer>& transfers) { |
| 646 | + Utils::StopWatch sw; |
| 647 | + Crypto::Checksum checksum; |
| 648 | + |
| 649 | + //! If message or tag is not supplied, provide it |
| 650 | + //! Also remove the checksum of the address if it's there |
| 651 | + |
| 652 | + for (auto& transfer : transfers) { |
| 653 | + if (transfer.getMessage().empty()) { |
| 654 | + transfer.setMessage(Type::Utils::rightPad(transfer.getMessage(), 2187, '9')); |
| 655 | + } |
| 656 | + |
| 657 | + if (transfer.getTag().empty()) { |
| 658 | + transfer.setTag(Type::Utils::rightPad(transfer.getTag(), 27, '9')); |
| 659 | + } |
| 660 | + |
| 661 | + if (checksum.isValid(transfer.getAddress())) { |
| 662 | + transfer.setAddress(checksum.remove(transfer.getAddress())); |
| 663 | + } |
| 664 | + } |
| 665 | + |
| 666 | + // Input validation of transfers object |
| 667 | + if (!isTransfersCollectionValid(transfers)) { |
| 668 | + throw Errors::IllegalState("Invalid transfer"); |
| 669 | + } |
| 670 | + |
| 671 | + //! validate input address |
| 672 | + if (!Type::isValidAddress(inputAddress)) { |
| 673 | + throw Errors::IllegalState("Invalid address"); |
| 674 | + } |
| 675 | + |
| 676 | + // validate remainder address |
| 677 | + if (!remainderAddress.empty() && !Type::isValidAddress(remainderAddress)) { |
| 678 | + throw Errors::IllegalState("Invalid bundle"); |
| 679 | + } |
| 680 | + |
| 681 | + //! Create a new bundle |
| 682 | + Bundle bundle; |
| 683 | + int totalValue = 0; |
| 684 | + std::vector<std::string> signatureFragments; |
| 685 | + std::string tag; |
| 686 | + |
| 687 | + //! Iterate over all transfers, get totalValue |
| 688 | + //! and prepare the signatureFragments, message and tag |
| 689 | + for (const auto& transfer : transfers) { |
| 690 | + int signatureMessageLength = 1; |
| 691 | + |
| 692 | + //! If message longer than 2187 trytes, increase signatureMessageLength (add 2nd transaction) |
| 693 | + if (transfer.getMessage().length() > MaxTrxMsgLength) { |
| 694 | + //! Get total length, message / maxLength (MaxTrxMsgLength trytes) |
| 695 | + signatureMessageLength += std::floor(transfer.getMessage().length() / MaxTrxMsgLength); |
| 696 | + |
| 697 | + //! copy msg |
| 698 | + std::string msgCopy = transfer.getMessage(); |
| 699 | + |
| 700 | + //! While there is still a message, copy it |
| 701 | + while (!msgCopy.empty()) { |
| 702 | + std::string fragment = msgCopy.substr(0, MaxTrxMsgLength); |
| 703 | + msgCopy = msgCopy.substr(MaxTrxMsgLength, msgCopy.length()); |
| 704 | + |
| 705 | + // Pad remainder of fragment |
| 706 | + signatureFragments.push_back(Type::Utils::rightPad(fragment, MaxTrxMsgLength, '9')); |
| 707 | + } |
| 708 | + } else { |
| 709 | + //! Else, get single fragment with MaxTrxMsgLength of 9's trytes |
| 710 | + signatureFragments.push_back( |
| 711 | + Type::Utils::rightPad(transfer.getMessage(), MaxTrxMsgLength, '9')); |
| 712 | + } |
| 713 | + |
| 714 | + //! get current timestamp in seconds |
| 715 | + long timestamp = sw.now().count(); |
| 716 | + |
| 717 | + //! If no tag defined, get 27 tryte tag. |
| 718 | + if (transfer.getTag().empty()) { |
| 719 | + tag = Type::Utils::rightPad(transfer.getTag(), 27, '9'); |
| 720 | + } |
| 721 | + |
| 722 | + //! Pad for required TagLength tryte length |
| 723 | + tag = Type::Utils::rightPad(tag, TagLength, '9'); |
| 724 | + |
| 725 | + //! Add first entry to the bundle |
| 726 | + bundle.addTransaction(signatureMessageLength, transfer.getAddress(), transfer.getValue(), tag, |
| 727 | + timestamp); |
| 728 | + |
| 729 | + //! Sum up total value |
| 730 | + totalValue += transfer.getValue(); |
| 731 | + } |
| 732 | + |
| 733 | + //! Get inputs if we are sending tokens |
| 734 | + if (totalValue == 0) { |
| 735 | + throw Errors::IllegalState("Invalid value transfer"); |
| 736 | + } |
| 737 | + |
| 738 | + long totalBalance = 0; |
| 739 | + |
| 740 | + for (const auto& balance : getBalances({ inputAddress }, 100).getBalances()) { |
| 741 | + totalBalance += std::atol(balance.c_str()); |
| 742 | + } |
| 743 | + |
| 744 | + // get current timestamp in seconds |
| 745 | + long timestamp = sw.now().count(); |
| 746 | + |
| 747 | + if (totalBalance > 0) { |
| 748 | + long toSubtract = -totalBalance; |
| 749 | + |
| 750 | + //! Add input as bundle entry |
| 751 | + //! Only a single entry, signatures will be added later |
| 752 | + bundle.addTransaction(securitySum, inputAddress, toSubtract, tag, timestamp); |
| 753 | + } |
| 754 | + |
| 755 | + //! Return not enough balance error |
| 756 | + if (totalValue > totalBalance) { |
| 757 | + throw Errors::IllegalState("Not enough balance"); |
| 758 | + } |
| 759 | + |
| 760 | + //! If there is a remainder value |
| 761 | + //! Add extra output to send remaining funds to |
| 762 | + if (totalBalance > totalValue) { |
| 763 | + long remainder = totalBalance - totalValue; |
| 764 | + |
| 765 | + // Remainder bundle entry if necessary |
| 766 | + if (remainderAddress.empty()) { |
| 767 | + throw Errors::IllegalState("No remainder address defined"); |
| 768 | + } |
| 769 | + |
| 770 | + bundle.addTransaction(1, remainderAddress, remainder, tag, timestamp); |
| 771 | + } |
| 772 | + |
| 773 | + bundle.finalize(Crypto::create(cryptoType_)); |
| 774 | + bundle.addTrytes(signatureFragments); |
| 775 | + |
| 776 | + return bundle.getTransactions(); |
| 777 | +} |
| 778 | + |
642 | 779 | /* |
643 | 780 | * Private methods. |
644 | 781 | */ |
@@ -749,6 +886,17 @@ Extended::signInputsAndReturn(const std::string& seed, const std::vector<input>& |
749 | 886 | return bundleTrytes; |
750 | 887 | } |
751 | 888 |
|
| 889 | +bool |
| 890 | +Extended::isTransfersCollectionValid(const std::vector<Transfer>& transfers) { |
| 891 | + for (const auto& transfer : transfers) { |
| 892 | + if (!transfer.isValid()) { |
| 893 | + return false; |
| 894 | + } |
| 895 | + } |
| 896 | + |
| 897 | + return true; |
| 898 | +} |
| 899 | + |
752 | 900 | } // namespace API |
753 | 901 |
|
754 | 902 | } // namespace IOTA |
0 commit comments