diff --git a/silkaj/constants.py b/silkaj/constants.py index 27fbae0e2ae1d0b93aba396cd638fc65c2a31c94..7aa8659fd033c80b6591235dcf64b6e41dcc65ed 100644 --- a/silkaj/constants.py +++ b/silkaj/constants.py @@ -22,7 +22,6 @@ G1_DEFAULT_ENDPOINT = "g1.duniter.org", "443" G1_TEST_DEFAULT_ENDPOINT = "g1-test.duniter.org", "443" CONNECTION_TIMEOUT = 10 ASYNC_SLEEP = 0.15 -SOURCES_PER_TX = 40 SUCCESS_EXIT_STATUS = 0 FAILURE_EXIT_STATUS = 1 BMA_MAX_BLOCKS_CHUNK_SIZE = 5000 diff --git a/silkaj/tx.py b/silkaj/tx.py index 36d3e12c9feb0d4c43ed6d19721d0c833e94ed4a..cc922f93d4e40e3dffbee6c18e9ea0785bf5f607 100644 --- a/silkaj/tx.py +++ b/silkaj/tx.py @@ -30,7 +30,6 @@ from silkaj import auth from silkaj import money from silkaj import tui from silkaj.constants import ( - SOURCES_PER_TX, MINIMAL_ABSOLUTE_TX_AMOUNT, MINIMAL_RELATIVE_TX_AMOUNT, CENT_MULT_TO_UNIT, @@ -292,9 +291,10 @@ async def transaction_confirmation( return tx -async def get_list_input_for_transaction(pubkey, TXamount): +async def get_list_input_for_transaction(pubkey, TXamount, outputs_number): listinput, amount = await money.get_sources(pubkey) - + # check max inputs. For now we deal only with one issuer + maxInputsNumber = max_inputs_number(outputs_number, 1) # generate final list source listinputfinal = [] totalAmountInput = 0 @@ -303,11 +303,18 @@ async def get_list_input_for_transaction(pubkey, TXamount): listinputfinal.append(input) totalAmountInput += money.amount_in_current_base(input) TXamount -= money.amount_in_current_base(input) - # if more than 40 sources, it's an intermediate transaction - if len(listinputfinal) >= SOURCES_PER_TX: - intermediatetransaction = True - break - if TXamount <= 0: + # if too much sources, it's an intermediate transaction. + if ( + len(listinputfinal) > maxInputsNumber + or len(listinputfinal) >= MAX_INPUTS_PER_TX + ): + if not (len(listinputfinal) >= MAX_INPUTS_PER_TX and TXamount <= 0): + intermediatetransaction = True + # if we reach the MAX_INPUTX_PER_TX limit, we send the interm.tx + # if we gather the good amount, we send the tx : + # - either this is no int.tx, and the tx is sent to the receiver, + # - or the int.tx it is sent to the issuer before sent to the receiver. + if len(listinputfinal) >= MAX_INPUTS_PER_TX or TXamount <= 0: break if TXamount > 0 and not intermediatetransaction: tools.message_exit("Error: you don't have enough money") @@ -323,9 +330,10 @@ async def handle_intermediaries_transactions( OutputbackChange=None, ): client = nt.ClientInstance().client + while True: listinput_and_amount = await get_list_input_for_transaction( - issuers, sum(tx_amounts) + issuers, sum(tx_amounts), (len(outputAddresses) + 1) ) intermediatetransaction = listinput_and_amount[2] diff --git a/tests/patched/money.py b/tests/patched/money.py index 8e52e77ac5abb50cc37aefa546f2d950b4a8b6c0..ff82f0ec2345efd8ba3c1bcd1158dd12fb183848 100644 --- a/tests/patched/money.py +++ b/tests/patched/money.py @@ -17,8 +17,9 @@ along with Silkaj. If not, see <https://www.gnu.org/licenses/>. # This file contains patched functions for testing purposes. -from silkaj.constants import G1_SYMBOL, SOURCES_PER_TX +from silkaj.constants import G1_SYMBOL from silkaj.money import amount_in_current_base +from silkaj.tx import MAX_INPUTS_PER_TX from duniterpy.documents.transaction import InputSource from patched.test_constants import mock_ud_value @@ -93,7 +94,7 @@ async def patched_get_sources(pubkey): elif patched_get_sources.counter == 1: listinput.append( InputSource( - amount=100 * SOURCES_PER_TX, # 100 * 40 = 4000 + amount=100 * MAX_INPUTS_PER_TX, # 100 * 46 = 4600 base=0, source="T", origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", @@ -112,7 +113,7 @@ async def patched_get_sources(pubkey): elif patched_get_sources.counter == 1: listinput.append( InputSource( - amount=mock_ud_value * SOURCES_PER_TX, # 40 UD = 40*314 = 12560 + amount=mock_ud_value * MAX_INPUTS_PER_TX, # 46 UD = 46*314 = 1444 base=0, source="T", origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", @@ -121,6 +122,18 @@ async def patched_get_sources(pubkey): ) max_ud = 4 max_tx = 20 + elif pubkey == "BdanxHdwRRzCXZpiqvTVTX4gyyh6qFTYjeCWCkLwDifx": + listinput.append( + InputSource( + amount=9600, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=0, + ) + ) + max_ud = 0 + max_tx = 0 else: max_ud = 0 max_tx = 0 diff --git a/tests/test_unit_tx.py b/tests/test_unit_tx.py index 3e998886b8bdff7d417bd2d77d82f9055e927113..4b2d9ad989cb1e6a45040b8ce285a936bb4dfa96 100644 --- a/tests/test_unit_tx.py +++ b/tests/test_unit_tx.py @@ -338,52 +338,112 @@ async def test_generate_transaction_document( # get_list_input_for_transaction() @pytest.mark.parametrize( - "pubkey, TXamount, expected", + "pubkey, TXamount, outputs_number, expected", [ - # less than 1 source - ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 99, (1, 100, False)), - # exactly one source - ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 100, (1, 100, False)), - # more than 1 source and no interm tx - ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 150, (2, 200, False)), - # all sources - ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 300, (3, 300, False)), - # too high amount + # no need for interm tx, around 1 source + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 99, 2, (1, 100, False)), + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 100, 2, (1, 100, False)), + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 101, 2, (2, 200, False)), + # no need for interm.tx, arbitraty number of sources + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 199, 2, (2, 200, False)), + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 200, 2, (2, 200, False)), + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 201, 2, (3, 300, False)), + # no need for interm.tx, around last available source + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 299, 2, (3, 300, False)), + ("CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 300, 2, (3, 300, False)), ( "CtM5RZHopnSRAAoWNgTWrUhDEmspcCAxn6fuCEWDWudp", 301, + 15, "Error: you don't have enough money", ), - # need for an intermediary tx - ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4100, (40, 4000, True)), - # no need for an intermediary tx, but the function still does it - ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4000, (40, 4000, True)), - # less than 1 UD source - ("2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", 200, (1, 314, False)), - # exactly 1 UD source - ("2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", 314, (1, 314, False)), - # all sources with UD sources - ("2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", 3140, (10, 3140, False)), - # too high amount + # Same tests with UD sources + # no need for interm tx, around 1 source ( "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", - 5000, + mock_ud_value - 1, + 2, + (1, mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + mock_ud_value, + 2, + (1, mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + mock_ud_value + 1, + 2, + (2, mock_ud_value * 2, False), + ), + # no need for interm.tx, arbitraty number of sources + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 5 * mock_ud_value - 1, + 2, + (5, 5 * mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 5 * mock_ud_value, + 2, + (5, 5 * mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 5 * mock_ud_value + 1, + 2, + (6, 6 * mock_ud_value, False), + ), + # no need for interm.tx, around last available source + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 10 * mock_ud_value - 1, + 1, + (10, 10 * mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 10 * mock_ud_value, + 2, + (10, 10 * mock_ud_value, False), + ), + ( + "2sq4w8yYVDWNxVWZqGWWDriFf5z7dn7iLahDCvEEotuY", + 10 * mock_ud_value + 1, + 1, "Error: you don't have enough money", ), + # high limit for input number + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4600, 2, (46, 4600, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4601, 2, (46, 4600, True)), + # many outputs + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 3999, 15, (40, 4000, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4000, 15, (40, 4000, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4001, 15, (41, 4100, True)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4600, 15, (46, 4600, True)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4601, 15, (46, 4600, True)), + # higher limit for outputs + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 99, 93, (1, 100, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 100, 93, (1, 100, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 101, 93, (2, 200, True)), + # 1 ouput should rarely occur, but we should test it. + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4599, 1, (46, 4600, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4600, 1, (46, 4600, False)), + ("HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", 4601, 1, (46, 4600, True)), # mix UD and TX source - ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 2500, (8, 2512, False)), - ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 7800, (25, 7850, False)), + ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 2500, 15, (8, 2512, False)), + ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 7800, 15, (25, 7850, False)), # need for interm tx - ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 12561, (40, 12560, True)), - # no need for interm tx but the function still does it - ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 12247, (40, 12560, True)), - # exactly 39 sources - ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 12246, (39, 12246, False)), + ("9cwBBgXcSVMT74xiKYygX6FM5yTdwd3NABj1CfHbbAmp", 14444, 15, (46, 14444, True)), + # 93 outputs, should send 1 input only + ("BdanxHdwRRzCXZpiqvTVTX4gyyh6qFTYjeCWCkLwDifx", 9100, 91, (1, 9600, False)), ], ) @pytest.mark.asyncio async def test_get_list_input_for_transaction( - pubkey, TXamount, expected, monkeypatch, capsys + pubkey, TXamount, outputs_number, expected, monkeypatch, capsys ): """ expected is [len(listinput), amount, IntermediateTransaction] or "Error" @@ -397,12 +457,16 @@ async def test_get_list_input_for_transaction( # testing error exit if isinstance(expected, str): with pytest.raises(SystemExit) as pytest_exit: - result = await tx.get_list_input_for_transaction(pubkey, TXamount) + result = await tx.get_list_input_for_transaction( + pubkey, TXamount, outputs_number + ) assert expected == capsys.readouterr() assert pytest_exit.type == SystemExit # testing good values else: - result = await tx.get_list_input_for_transaction(pubkey, TXamount) + result = await tx.get_list_input_for_transaction( + pubkey, TXamount, outputs_number + ) assert (len(result[0]), result[1], result[2]) == expected @@ -414,7 +478,7 @@ async def test_get_list_input_for_transaction( ( key_fifi, "HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", - (100, 100), + [100] * 2, [ "DBM6F5ChMJzpmkUdL5zD9UXKExmZGfQ1AgPDQy4MxSBw", "4szFkvQ5tzzhwcfUtZD32hdoG2ZzhvG3ZtfR61yjnxdw", @@ -442,14 +506,12 @@ async def test_get_list_input_for_transaction( False, ), ), - # test 2 : with one amounts/outputs and no outputbackchange, need for intermediary transaction. + # test 2 : with 15 amounts/outputs and no outputbackchange, need for intermediary transaction. ( key_fifi, "HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", - (4001,), - [ - "4szFkvQ5tzzhwcfUtZD32hdoG2ZzhvG3ZtfR61yjnxdw", - ], + [350] * 14, # total 4900, pubkey has 5300 + ["4szFkvQ5tzzhwcfUtZD32hdoG2ZzhvG3ZtfR61yjnxdw"] * 14, "Test comment", "HcRgKh4LwbQVYuAc3xAdCynYXpKoiPE6qdxCMa8JeHat", ( @@ -734,8 +796,50 @@ async def test_get_list_input_for_transaction( origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", index=39, ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=40, + ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=41, + ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=42, + ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=43, + ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=44, + ), + InputSource( + amount=100, + base=0, + source="T", + origin_id="1F3059ABF35D78DFB5AFFB3DEAB4F76878B04DB6A14757BBD6B600B1C19157E7", + index=45, + ), ], - 4000, + 4600, True, ), ),