Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Accumulated balance 0 is less than threshold 1 #34

Closed
CaixiangFan opened this issue Mar 22, 2021 · 5 comments
Closed

Accumulated balance 0 is less than threshold 1 #34

CaixiangFan opened this issue Mar 22, 2021 · 5 comments
Labels
bug Something isn't working

Comments

@CaixiangFan
Copy link

CaixiangFan commented Mar 22, 2021

Bug description

The private tangle goes to an error when trying to issue a non-zero transfer using Pyota API api.send_transfer(transfers=[tx]):
iota.adapter.BadApiResponse: Accumulated balance 0 is less than threshold 1 (exc.context contains more information).

While other API calls work correctly such as send_transfer a zero-transaction and getbalance.

Docker and docker-compose version

Docker version 20.10.5;
Docker-compose version 1.28.5, build c4eb3a1f
API version: PyOTA==2.1.0

Steps To reproduce the bug

Explain how the maintainer can reproduce the bug.

  1. Run one-click-tangle to launch a private tangle
  2. At client side, install Pyota and use send_transfer() to send a non-zero transaction. The seed is from the generated node.seed file in the tangle node. It has an address with 2779530283277761 i as shown in the initial snapshot file.
  3. Above error happened.

Errors

iota.adapter.BadApiResponse: Accumulated balance 0 is less than threshold 1 (exc.context contains more information).

Client API code snippets

from iota import Iota, Address, TryteString, ProposedTransaction, Tag
from iota.crypto.types import Seed
my_seed = Seed(b'GNKDGCZRZJDRAOTHMBTINCBI9A9ZNHCZGCKMEYGODDJFMPPSEJLNMIAHLTOIFATUIGLNEVZS9TTUTWIMX')

api = Iota(
adapter='http://127.0.0.1:14265',
seed=my_seed,
testnet=True,
)

receiver = Address(b'B9WSEPNPHMEIWIAUQIKUVBGKSBTIVZHFKDNWAVNWTRQUKBUWBE9VUME9DGFEHVAWNJZMEBNCOURPHYDAB')

tx = ProposedTransaction(
address=receiver,
value=1,
message=TryteString.from_unicode('I just sent you 1i, use it wisely!'),
tag=Tag('VALUETX'),
)

response = api.send_transfer(transfers=[tx], security_level=2)

@CaixiangFan CaixiangFan added the bug Something isn't working label Mar 22, 2021
@peterokwara
Copy link
Contributor

You can try this code

from iota.crypto.types import Seed

my_seed = 'GPYPXITPCFFAYHB9XTROZBJXKKXHJSQQBNAAFRMJ9ADH9NSGUMWMFDU9EGB9ONI9KBIVYTBEBIX9ZHMDC'

api = Iota(adapter = 'http://localhost:14265', seed = my_seed)

receiver = 'XESQFFFMEKVWMYKPTBXIBRQHZLJGR9WXEOHMUOLJLLBGEPKRHNPZPKJOGQUPEIQTDKWTRXTCCQWMWQDIC'

tx = ProposedTransaction(
    address=Address(receiver),
    message=TryteString.from_unicode('This transaction should include 1i!'),
    tag=Tag('VALUETX'),
    value=100
)

input = api.get_inputs(start=0, stop=3)
print(input)
inputs = input['inputs']
tx = api.prepare_transfer(transfers=[tx], inputs=inputs)

result = api.send_trytes(tx['trytes'], depth=3, min_weight_magnitude=9)

print(result)

The only change in this code is that we have get_inputs() function https://pyota.readthedocs.io/en/latest/extended_api.html#get-inputs that gets all inputs of a seed and returns together with total balance.

That is then used in the prepare_transfer() https://pyota.readthedocs.io/en/latest/extended_api.html#prepare-transfer as a list of addresses used to fund the transfer.

Reason being, as described in this issue iotaledger/iota.py#212 and here https://iota.stackexchange.com/questions/658/how-many-addresses-do-i-have-to-reattach-after-a-snapshot/735#735 this is because iota wallets are stateless. When you enter the seed, the wallet needs to find out what addresses you have already used, and the total balance.

With snapshots, transactions are normally deleted from the tangle, in this case with the private tangle. Only that the wallet finds only one address with no transactions and assumes since it's a new seed you should have zero balance.

The code above works to avoid the issue. This also works in case you are using javascript to do this.

@CaixiangFan
Copy link
Author

CaixiangFan commented Apr 23, 2021

@peterokwara Many thanks for the suggestions and explanations. It works when I got inputs manually and sent the transfers.
But it only worked for several (e.g., 3) consecutive sends, then the balances from get_inputs API became 0.

However, the get_account_data() API can provide results with correct non-zero balances.
Here is an example:

$ python getaccountdata.py JSFCIXUGZADG9UWIIXKXKQXHNWPUJLWTOUJWMVMLSOI9BR9ONMSOBTNSN9XHITHHUQR9HVLFBIPHJUZEB
{'addresses': [Address(b'B9WSEPNPHMEIWIAUQIKUVBGKSBTIVZHFKDNWAVNWTRQUKBUWBE9VUME9DGFEHVAWNJZMEBNCOURPHYDAB'), Address(b'OFGEFUL9RKQONERLCNDAKDFMHSOUOETUSFLYPARPZXJA9USGDM9BQLAAMZIXNNVQFZZYYNXCVOYLMRNAX'), Address(b'SHOEDPNANP9PEEVWKAYKYTE9RSIAUETUXGDYVK9EULMSBISIFMNHFAKIDAXBMNPVGIDPIQBDETCRKDRBB'), Address(b'HAUYYEWDWEDB9J9OLJDCGIKFJIITIWKYZP9TZRNLCEASVNTDGCVUTLEOUVUHDJ9VTMNGZWHJCVYGLPCPB')], 'balance': 200, 'bundles': [<iota.transaction.base.Bundle object at 0x10dbc8150>, <iota.transaction.base.Bundle object at 0x10dbc2f10>, <iota.transaction.base.Bundle object at 0x10dba5650>, <iota.transaction.base.Bundle object at 0x10dbcb890>, <iota.transaction.base.Bundle object at 0x10dba6cd0>, <iota.transaction.base.Bundle object at 0x10dba3d10>]}

$ python getinputs.py JSFCIXUGZADG9UWIIXKXKQXHNWPUJLWTOUJWMVMLSOI9BR9ONMSOBTNSN9XHITHHUQR9HVLFBIPHJUZEB
{'inputs': [], 'totalBalance': 0}

Do you have any suggestions on why this issue happens and the difference between these two API call results?
Thanks.

@peterokwara
Copy link
Contributor

From what I just tested now, the index is set from 0 to 3

input = api.get_inputs(start=0, stop=3)

And I sent out multiple transactions. And when I do getInputs I get

{'inputs': [Address(b'XESQFFFMEKVWMYKPTBXIBRQHZLJGR9WXEOHMUOLJLLBGEPKRHNPZPKJOGQUPEIQTDKWTRXTCCQWMWQDIC')], 'totalBalance': 100}

But when I set the index from 0 to 10, 10 being a higher random number, I get

input = api.get_inputs(start=0, stop=10)
{'inputs': [Address(b'XESQFFFMEKVWMYKPTBXIBRQHZLJGR9WXEOHMUOLJLLBGEPKRHNPZPKJOGQUPEIQTDKWTRXTCCQWMWQDIC'), Address(b'OYQTSWAAFQX9PKGXUJPKWZKMYTGDDHGMGTCENASSHDJI9UQFBWVFEEKCEVPNSEHMLQYHLFBUJPDNYQWKZ')], 'totalBalance': 2779530283277761}

I have a rough idea about why this happens, will get back to you when I get an answer, and after doing multiple tests.

@peterokwara
Copy link
Contributor

Yeah. Best practice would be to keep track of the index, probably incrementing after each and every transaction.

With chrysalis this changes because you can send and receive with the same address. You only need to use one address. Though you need to wait for confirmation, otherwise you get conflicts in the ledger state.

@CaixiangFan
Copy link
Author

@peterokwara Thanks a lot! Based on your suggestions, I kept both start and stop index default, so that this method will not stop until it finds an unused address. It works now.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants