Skip to content

Support Ledger clear signing #1603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: development
Choose a base branch
from

Conversation

franciszekjob
Copy link
Collaborator

@franciszekjob franciszekjob commented May 12, 2025

Closes #1473

Introduced changes

Add support for clear singing in LedgerSigner. Now we're getting displayed tx details before approveing/rejecting it.

  • Add LedgerSigningMode
  • Use clear signing by default
clear_sign_example.mov

  • This PR contains breaking changes

Comment on lines -42 to -49
# Ledger constants
STARKNET_CLA = 0x5A
EIP_2645_PURPOSE = 0x80000A55
EIP_2645_PATH_LENGTH = 6
PUBLIC_KEY_RESPONSE_LENGTH = 65
SIGNATURE_RESPONSE_LENGTH = 65
VERSION_RESPONSE_LENGTH = 3

Copy link
Collaborator Author

@franciszekjob franciszekjob May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: These variables aren't used across multiple files so let's keep them in the dedicated (ledger) one.

Comment on lines -161 to +206
recipient_address = (
0x1323CACBC02B4AAED9BB6B24D121FB712D8946376040990F2F2FA0DCF17BB5B
)
recipient_address = 0x123
Copy link
Collaborator Author

@franciszekjob franciszekjob May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: 0x123 is better as an example address.

@@ -2,10 +2,13 @@
"version": 1,
"rules": [
{
"text": "Confirm Hash to sign",
"conditions": [],
"text": "Blind igning",
Copy link
Collaborator Author

@franciszekjob franciszekjob May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: It's not a typo - for some reason that's how ledger displays the text
image

Comment on lines +17 to +46
@pytest.mark.parametrize(
"transaction",
[
InvokeV3(
version=3,
signature=[],
nonce=1,
resource_bounds=MAX_RESOURCE_BOUNDS_SEPOLIA,
calldata=[
1,
2009894490435840142178314390393166646092438090257831307886760648929397478285,
232670485425082704932579856502088130646006032362877466777181098476241604910,
3,
0x123,
100,
0,
],
sender_address=0x123,
),
DeployAccountV3(
class_hash=0x123,
contract_address_salt=0x123,
constructor_calldata=[1, 2, 3],
version=3,
signature=[],
nonce=0,
resource_bounds=MAX_RESOURCE_BOUNDS_SEPOLIA,
),
],
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: We can't use mocks for clear singing as under the hood we're converting values from tx fields to bytes, and we would get TypeError: can't concat Mock to bytes.

@franciszekjob franciszekjob marked this pull request as ready for review May 13, 2025 08:05
@franciszekjob franciszekjob requested a review from ddoktorski May 13, 2025 08:52
DEVNET_SHA: fc5a2753a2eedcc27eed7a4fae3ecac08c2ca1b4
LEDGER_APP_SHA: db93a5e33d00bd44752fdc8c07aefcffa08d91c3
LEDGER_APP_DEV_TOOLS_SHA: a037d42181f4bed9694246256e2c9e2a899e775c302a9c6482c81f87c28e1432
DEVNET_SHA: fc5a2753a2eedcc27eed7a4fae3ecac08c2ca1b4 # v0.3.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but why devnet is not updated to the latest version?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing forced us to bump it, so it wasn't urgent. Bumped it to v0.4.1.

@@ -52,13 +56,20 @@ def get_start_devnet_command(devnet_port: int, fork_mode: bool = False) -> List[
]
)

if predeclare_argent:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why not to predeclare always?

Copy link
Collaborator Author

@franciszekjob franciszekjob May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like nothing prevents use from having it always predeclared. Will change it.


def sign_transaction(self, transaction: AccountTransaction) -> List[int]:
if self.signing_mode == LedgerSigningMode.CLEAR:
if isinstance(transaction, DeclareV3):
raise ValueError("DeclareV3 signing is not supported bye LedgerSigner")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why declare is not supported?

Copy link
Collaborator Author

@franciszekjob franciszekjob May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, haven't digged into the specific reason but maybe it's related with device hardware limits (considering that declare tx includes contract class) or security?

Comment on lines +211 to +223
account_address_bytes = contract_address.to_bytes(32, byteorder="big")
chain_id_bytes = self.chain_id.to_bytes(32, byteorder="big")
nonce_bytes = tx.nonce.to_bytes(32, byteorder="big")
common_tx_fields = tx.get_common_fields(
tx_prefix=TransactionHashPrefix.DEPLOY,
address=contract_address,
chain_id=self.chain_id,
)
da_mode_hash_bytes = common_tx_fields.get_data_availability_modes().to_bytes(
32, byteorder="big"
)
class_hash_bytes = tx.class_hash.to_bytes(32, byteorder="big")
salt_bytes = tx.contract_address_salt.to_bytes(32, byteorder="big")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe some kind of a helper function/struct to convert V3 transactions to the same struct but with fields as bytes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, potentially we could add a method for txs classes which would return their values as bytes, but that would require additional struct as the return type (cause returning as tuple of bytes wouldn't be very convenient). Tbh I'm not sure if adding extra code would improve current state, as now it's fairly simple and readable. Wdyt?

Comment on lines +463 to +471
if application_name == "LedgerW":
application_bytes = (
(43).to_bytes(1, byteorder="big")
+ (206).to_bytes(1, byteorder="big")
+ (231).to_bytes(1, byteorder="big")
+ (219).to_bytes(1, byteorder="big")
)
else:
application_bytes = _string_to_4byte_hash(application_name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this logic?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like these four integers are predefined for LedgerW and in other cases we just generate the representation.
I referred to https://github.com/starknet-io/starknet.js/blob/050616221a58545d79eece7040b4abc205c220e0/src/signer/ledgerSigner221.ts#L644

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update LedgerSigner
2 participants