-
Notifications
You must be signed in to change notification settings - Fork 188
Conversation
To support SendMany
Supports sendmany
To support sendmany
@ixje Thank you for telling me about |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR seems to have a lot of code duplication from #596 but then for the prompt. It's best to split the common code pieces and make that callable from both the prompt and RPC side.
fix compatibility issues
for compatibility
Merge CoZ Development into jseagrave21 SendMany_Prompt
- implement `construct_send_basic`, `construct_send_many`, and `process_transaction`
- Adds tests for `construct_send_basic`, `construct_send_many`, and overall tests - Improves test coverage
- implement new `send` and `sendmany` functions
- Fix sendmany token test - Add tests for bad precision - Add weird outgoing amount test - Add 'could not send' test
@ixje This PR is ready for another review. Please let me know if I am on the right track. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not looked into your test-cases at this point because I feel that the requested changes need to be addressed first. I'm assuming many of the tests now pass because you use a catch-all try/except
structure that filters out some of the real exceptions that should be addressed (like the return signature)
neo/Prompt/Utils.py
Outdated
if '--change-addr=' in item: | ||
to_remove.append(item) | ||
try: | ||
change_addr = item.replace('--change-addr=', '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the return of this functions expects at best 1 change_addr
then we do not need the to_remove
list and loop over it. We can do something like:
for item in params:
change_addr = None
if '--change-addr' in item:
params.remove(item)
change_addr = item.replace('--change-addr=','')
return params, change_addr
This way we stop the moment we found the a change address and avoid unnecessary looping over the remaining params.
What is the Try/except
catching exactly? It's generally bad practice to use Exception
(==catch all) and we should prefer being more specific (like ValueError
or TypeError
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The format for get_change_addr
followed the example of the pre-existing get_from_addr
. I will work on improving these utility functions.
neo/Prompt/Utils.py
Outdated
return params, change_addr | ||
|
||
|
||
def get_outgoing(params): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comments as for get_change_addr
- if we only return 1
outgoing
we don't need to push 1 item in a list. - try to make the exception specific
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I addressed this issue.
neo/bin/prompt.py
Outdated
construct_and_send(self, self.Wallet, arguments) | ||
try: | ||
contract_tx, scripthash_from, fee, owners, user_tx_attributes = construct_send_basic(self, self.Wallet, arguments) | ||
except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what specific errors are we catching?
neo/bin/prompt.py
Outdated
def do_send_many(self, arguments): | ||
try: | ||
contract_tx, scripthash_from, scripthash_change, fee, owners, user_tx_attributes = construct_send_many(self, self.Wallet, arguments) | ||
except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
neo/Prompt/Commands/Send.py
Outdated
return False | ||
|
||
output = TransactionOutput(AssetId=assetId, Value=f8amount, script_hash=scripthash_to) | ||
contract_tx = ContractTransaction(outputs=[output]) | ||
return contract_tx, scripthash_from, fee, owners, user_tx_attributes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the return signature of a function must be concise. Meaning you can't have success return a 5 argument tuple while failure returns a single boolean. This will exceptions when you try to use the command as follows:
contract_tx, scripthash_from, fee, owners, user_tx_attributes = construct_send_basic(prompt, wallet, bad_args)
bad_args
will cause the function to returns False
and then you'll get an exception saying something like (from the top of my head):
TypeError: 'bool' object is not iterable
because it's expecting an tuple (which is iterable) to unpack 5 values from and instead gets a boolean
neo/Prompt/Commands/Send.py
Outdated
|
||
print("sending with fee: %s " % fee.ToString()) | ||
return contract_tx, scripthash_from, scripthash_change, fee, owners, user_tx_attributes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same remark on function return signature as before
for compatibility
for compatibility
for compatibility
for compatibility
- test new `send` and `sendmany` implementation
- Implement new `send` and `sendmany` methods
- Remove unnecessary try/except logic
@ixje Okay, this PR is ready for another review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need some input how to correctly operate the command as I can't seem to get it working. I want to try using and abusing it to see it's behaviour
neo/bin/prompt.py
Outdated
@@ -142,6 +142,7 @@ class PromptInterface: | |||
'wallet split {addr} {asset} {unspent index} {divide into number of vins}', | |||
'wallet close', | |||
'send {assetId or name} {address} {amount} (--from-addr={addr}) (--fee={priority_fee})', | |||
'sendmany (--outgoing={number outgoing}) (--change-addr={addr}) (--from-addr={addr}) (--fee={priority_fee})', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to think of a clearer way to explain this function. I had to read the code to understand that outgoing
is a number that collects the outputs_array
of the C# description. For command arguments placing something between ()
parentheses means it's an optional argument. I'm pretty sure that's not the case here. I also can't get it to work:
neo> sendmany 0 neo neo
Asset id not found
neo> sendmany 1 neo neo
Asset id not found
neo> sendmany 1 neo
Not enough arguments
neo> sendmany 1 neo wut
Asset id not found
neo> sendmany
Not enough arguments
neo> sendmany --outgoing=0 neo bla
Asset id not found
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--outgoing is the only required command because it sets up how many iterations (transactions) prompt will ask for. I will update with what might be a better description. The test send_many_good_simple
illustrates how the function should work https://github.com/jseagrave21/neo-python/blob/b528e230e64ead9da59ed696f14023b6d5039222/neo/Prompt/Commands/tests/test_send_command.py#L336
You can see that after neo> sendmany
the only arg passed is 2
https://github.com/jseagrave21/neo-python/blob/b528e230e64ead9da59ed696f14023b6d5039222/neo/Prompt/Commands/tests/test_send_command.py#L340
Everything that follows is prompted.
neo/Prompt/Commands/Send.py
Outdated
change_address=None, | ||
fee=fee, | ||
from_addr=scripthash_from) | ||
def process_transaction(prompter, wallet, contract_tx, scripthash_from=None, scripthash_change=None, fee=None, owners=None, user_tx_attributes=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please remove the prompter
argument. It's not used. Same goes for all other functions in the Send
file, they have prompter
argument that's not used
- make tests compatible with Send.py without `prompter` argument
- remove the `self` argument from `construct_send_basic`, `construct_send_many`, `process_transaction`, and `parse_and_sign`
- remove `get_outgoing` logic and replace with `get_arg`
- update for simplified outgoing logic
- fix indentation so `sendmany` iterates correctly
@ixje Okay, I think this PR is ready for another review. Below is what I envision a complex implementation of
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there, just a few little touch ups
@@ -85,13 +85,14 @@ def get_asset_id(wallet, asset_str): | |||
|
|||
|
|||
def get_asset_amount(amount, assetId): | |||
f8amount = Fixed8.TryParse(amount) | |||
f8amount = Fixed8.TryParse(amount, require_positive=True) | |||
if f8amount is None: | |||
print("invalid amount format") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove this print statement, or change it to logger.debug()
. Reason being is that we get double error messages:
Outgoing Number 1
Asset to send: neo
Address to: AGBjDZDoRgkBnxPV6N8dH66h2mwZGCwJ6B
Amount to send: -1
invalid amount format
invalid amount
neo> quit
the alternative is to remove the "print(invalid amount" in Send.py
itself. Either way we shouldn't have 2 error messages for the same problem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is the utility function I will leave this message and use logger.debug()
for the message in Send.py
.
if f8amount is None: | ||
print("invalid amount format") | ||
return False | ||
|
||
elif f8amount.value % pow(10, 8 - Blockchain.Default().GetAssetState(assetId.ToBytes()).Precision) != 0: | ||
print("incorrect amount precision") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is the utility function I will leave this message and logger.debug()
for the message in Send.py.
neo/Prompt/Commands/Send.py
Outdated
|
||
scripthash_to = lookup_addr_str(wallet, address_to) | ||
if scripthash_to is None: | ||
print("invalid address") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we get a double invalid error message; please keep only one (see the description I gave regarding double value
error messages as well, I noticed that one first so was more verbose in my description there)
Address to: wertyu
Not correct Address, wrong length.
invalid address
self.assertTrue(res) # verify successful tx | ||
|
||
json_res = res.ToJson() | ||
self.assertEqual(self.watch_addr_str, json_res['vout'][0]['address']) # verify correct address_to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it just me going crazy or have i seen this before somewhere? It feels like I'm reviewing something I've done before in another PR. Note this is not a request to change something, I think I just seem to have lost track
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably. I derived these tests from my learning experience from implementing sendmany
for JsonRpcApi
|
||
self.assertEqual(1, len(res.Attributes)) | ||
self.assertTrue(res) | ||
self.assertEqual(2, len(res.Attributes)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please add a comment above saying:
By default the script_hash of the transaction sender is added to the TransactionAttribute list, therefore the Attributes length is `count` + 1
We only have to do this once, but it's something that raises eyebrows if you read this code without it. Because it looks like you add just 1 attribute and then test for 2
for compatibility
Merge CoZ Development into jseagrave21 SendMany_Prompt
- transfers redundant `print` notifications to `logger.debug()`
- add clarifying comment
Okay, in addition to your requested changes I also sent the |
Thanks! 🥇 |
nm, I found a new option in the github web UI for squashing PRs 💯 |
What current issue(s) does this address, or what feature is it adding?
Adds
sendmany
feature to Prompt.py including options forfrom-addr
andchange-addr
and all previous supported features fromsend
command (e.g. support for multi-sig wallets and user attributes)How did you solve this problem?
Trial and Error
How did you make sure your solution works?
Quite a bit of personal testing on privatenet and implemented a few tests.
NOTE: A couple of tests rely on
WalletFixtureTestCase
Are there any special changes in the code that we should be aware of?
No
Please check the following, if applicable:
make lint
?make test
?CHANGELOG.rst
? (if not, please do)