-
-
Notifications
You must be signed in to change notification settings - Fork 194
Add cardano support to trezorctl and some tests #300
Conversation
@matejcik I can leave this for you to review, right? |
trezorlib/cardano.py
Outdated
|
||
|
||
@session | ||
def cardano_sign_transaction(client, transaction): |
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.
please rename to sign_tx
("tx" instead of "transaction", no "cardano" prefix)
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.
also the signature is not great, see comments in trezorctl
and on create_sign_transaction_message
.
In general, these functions should consume either simple values, or filled out protobuf messages. In this case, best I could come up with is:
def sign_tx(client, inputs: List[CardanoTxInputType], outputs: List[CardanoTxOutputType], transactions: List[bytes])
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.
fixed
trezorctl
Outdated
@click.pass_obj | ||
def cardano_sign_transaction(connect, file): | ||
client = connect() | ||
signed_transaction = cardano.cardano_sign_transaction(client, transaction=json.load(file)) |
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.
you should not put raw dict into the API call.
Instead, something like:
transaction = cardano.create_sign_tx(json.load(file))
signed_tx = cardano.sign_tx(client, transaction)
Also, how can the user create the JSON?
I expect there is a separate tool / script that generates it, but can it include the appropriate BIP32 paths?
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.
fixed with you metioned suggestion bellow
trezorlib/cardano.py
Outdated
|
||
@session | ||
def cardano_sign_transaction(client, transaction): | ||
msg = create_sign_transaction_message(transaction) |
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 should not be part of this function, see comment in trezorctl
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.
fixed
trezorlib/cardano.py
Outdated
ack_message = create_tx_ack_message(transaction.get('transactions'), tx_index) | ||
response = client.call(ack_message) | ||
|
||
return { |
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.
any reason to return a dict, as opposed to a tuple (hash, body)
?
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.
It is so user will see what is hash and what is body without looking at code. if it is only tuple you will have hard times to identify body vs hash as both looks same
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 is not user-facing function though. please return a tuple and do the conversion in trezorctl
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.
fixed
trezorlib/cardano.py
Outdated
|
||
|
||
def create_sign_transaction_message(transaction) -> messages.CardanoSignTransaction: | ||
if not all(transaction.get(k) for k in ('inputs', 'outputs', 'transactions')): |
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.
please extract the lists of required fields to the top level:
REQUIRED_FIELDS_TRANSACTION = ("inputs", "outputs"...
REQUIRED_FIELDS_INPUT = (...
REQUIRED_FIELDS_OUTPUT = (...
Don't repeat the fields in the ValueError
either.
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.
Fixed except output fields as there is only amount required and one other but it does not matter which one - well there must be at least one address_n (at least in one of the outputs)
trezorlib/cardano.py
Outdated
|
||
outputs = [] | ||
for output in transaction.get('outputs'): | ||
if not output.get('amount') or not(output.get('address') or output.get('path')): |
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.
please use the same all(...)
check as above
alternately, a better approach could be ensure_required_fields(dict, REQUIRED_FIELDS)
helper that raises the ValueError. In that case, you could even put the list of required fields into the error messages
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.
Not sure how could I use all
whene there are not || not ( || )
which is same as not || (not && not)
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.
ok, my bad, didn't notice the nesting
trezorlib/cardano.py
Outdated
while response.tx_index is not None: | ||
tx_index = response.tx_index | ||
|
||
ack_message = create_tx_ack_message(transaction.get('transactions'), tx_index) |
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 create_tx_ack_message
is sort of pointless, esp. if you get transactions
directly as an argument. please inline it here.
(also maybe verify that the requested index makes sense? but AFAICT the index
is pointless anyway, so maybe it doesn't matter)
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.
fixed without index verification
import pytest | ||
|
||
from .common import TrezorTest | ||
from .conftest import TREZOR_VERSION |
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.
you don't seem to be using TREZOR_VERSION
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.
removed
class TestMsgCardanoSignTransaction(TrezorTest): | ||
|
||
def test_cardano_sign_transaction(self): | ||
self.setup_mnemonic_nopin_nopassphrase() |
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.
ISTM this test could be vastly simplified if you used set_expected_responses
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 want to used that but I was unsure how to use it when I need to do swipes
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.
Ah, the swipes thing again! Yes, it makes sense in that case. We need a better infrastructure for simulating swipes.
I have reviewed the changes. Please see comments and make modifications where appropriate. In general, my main question is, how do you expect this to be used, from the user side? Is there a tool/service that generates the appropriate JSON data? If not, then, I suppose we'll keep it like this for now. I'm working on some changes that would make it more useful; namely, converting a dict to protobuf directly. In any case, unless there are good reasons against, please go with my suggestion for the
If the format comes from somewhere else, I'd keep the |
Also please add the remaining tests (sign/verify message, get public key i think?) |
@matejcik I tried to fix all your comments and also added tests... About that JSON tool - there is no such a tool. If somebody want to use it then they could look here: There is our own code for creating tx or they can take a look to connect v5. The CI is failing because of some other test fails to fetch some data from internet - I guess that we did not break it |
trezorlib/cardano.py
Outdated
ack_message = messages.CardanoTxAck(transaction=transaction_data) | ||
response = client.call(ack_message) | ||
|
||
return ( |
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.
return response.tx_hash, response.tx_body
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 just returned response because it is CardanoSignedTx message now. Is that ok?
Tests look reasonable. Please fix my latest comments, I'll look over the whole thing again tomorrow. Travis failure is on our side; if you rebase on top of current master, it should go away. |
Please also modify the signing test where now CardanoSignedTx message is returned (trezor/trezor-core@e657397) (if needed). |
I fixed comments and also did the rebase. |
@ddeath we would like to unify what derivation paths are sent to which coins. We came up with this doc. We would like Cardano wallets to send
|
|
I finally got to review the current state. It looks mostly OK. Two things:
|
Ok, agreed. |
5355759
to
0d2575b
Compare
@tsusanka I changed test so it reflect the derivation paths. Please check if that is what you mean. Also I overlooked the get_address and sign_transaction derivation paths and would suggest to change it to @matejcik added invalid signatures tests. The test should be passing on master, but they are not because there was introduced bugs couple days ago in this commit where the name of messages are incorrectly swaped and also in this commit where the So not sure if I should mark it as xfail. I will create a PR for adding derivation scheme v2 in couple of hours where I fixed it already. But not sure about your workflow so please just point me out what is the way to do it in this situation. Thanks |
Btw for further reference it would be great if you didn't squash the commit every time, so we could see changes from last time and not have to read through the whole thing. If your code is in core, and the tests are supposed to pass, then no xfail is needed. xfail is for cases when the tests are submitted ahead of the device code. |
|
||
@pytest.mark.cardano | ||
@pytest.mark.skip_t1 # T1 support is not planned | ||
class TestMsgCardanoGetPublicKey(TrezorTest): |
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.
please rename to match test contents
|
||
@pytest.mark.cardano | ||
@pytest.mark.skip_t1 # T1 support is not planned | ||
class TestMsgCardanoGetPublicKey(TrezorTest): |
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.
please rename test and method names, and consider using pytest.mark.parametrize
@pytest.mark.skip_t1 # T1 support is not planned | ||
class TestMsgCardanoGetPublicKey(TrezorTest): | ||
|
||
def test_cardano_get_public_key(self): |
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.
also this
while you're at it, consider using @pytest.mark.parametrize
to simplify test code here, see e.g. test_cancel
|
||
def test_cardano_get_public_key(self): | ||
self.client.load_device_by_mnemonic( | ||
mnemonic='plastic that delay conduct police ticket swim gospel intact harsh obtain entire', |
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.
Any reason you're using this mnemonic? If not, please regenerate the sigs with the default all all all...
(a good reason would be, e.g., if these were officially documented test vectors)
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 was choosing this mnemonic because the same is in trezor-core
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.
In that case let's change it to the default all all all...
. It should be changed in trezor-core too, but we'll probably do that later along with other currencies that use custom mnemonics for tests.
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 can change it as I will be removing / changing all the tests because of change of derivation scheme in the next PR.
signature = sign_message(self.client, parse_path(derivation_path), message) | ||
assert expected_signature == hexlify(signature.signature).decode('utf8') | ||
|
||
incorrect_messages = [ |
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.
FWIW you don't need to test invalid sigs here, because you're already testing against an expected value; it's very unlikely that a sign_message
would produce the particular signature that you're testing it's not producing.
(as opposed to the verify_message
method, where you need a test that it can fail, that it doesn't return True
for every input)
unless of course these are vectors for some known failure mode
trezorlib/cardano.py
Outdated
REQUIRED_FIELDS_INPUT = ('path', 'prev_hash', 'prev_index', 'type') | ||
|
||
|
||
@expect(messages.CardanoAddress) |
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 should probably extract the @field("address")
directly, right?
assert address == '2w1sdSJu3GVhMmEYeGYEPWahV1V17pFw59GfgqjSRqa6x1rKFxbyCZrQWLe78xdSx3zyed6DrrN5yMgoY7ST2vJeaMzUDB7W3WG' | ||
address = get_address(self.client, parse_path("m/44'/1815'/0'/0/2")).address | ||
assert address == '2w1sdSJu3GVeHCDfy3mjq8RkzkN3Vh7Di3cB8NRzkwkLQ2FAjxX1kvkNdP9hNBzyBVEJdeWwyb5GfFYXgKe7rPgvWj2QD8FE4W3' | ||
address = get_address(self.client, parse_path(path)).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.
ISTM this will fail now that get_address
returns the address directly?
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.
Yes it failed. Fixed. Also replaced custom mnemonic for all all...
In the meantime, I merged a big change into master. This should affect you very little, although you will probably get merge conflicts in IMHO the safest way to resolve them is simply copy-paste your code from pre-merge Another change that affects you is that afterwards, run |
8d9f805
to
46b4327
Compare
@tsusanka please ignore last conversation about |
@matejcik should be fixed now |
Ok so |
IMHO it is correct now and the tests seem to use correct paths. |
I am ok with the current paths |
Update submodule to trezor official one
1640385
to
44e3d73
Compare
@matejcik added changes for cadrano derivation path v2 and marked all test as xfail |
Neat, merging. Now let's see what happens with core :) |
Added device tests for get_address and sign_transaction calls.
To the trezorctl was also added get_public_key, sign and verify message