diff --git a/client/pyproject.toml b/client/pyproject.toml index 775ff53..7b1e8f6 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -29,6 +29,7 @@ requires-python = ">=3.7" dependencies = [ "ragger[speculos]", "simple-rlp", + "pysha3", ] [tools.setuptools] diff --git a/client/src/ledger_app_clients/ethereum/client.py b/client/src/ledger_app_clients/ethereum/client.py index 2c33026..f8b98e2 100644 --- a/client/src/ledger_app_clients/ethereum/client.py +++ b/client/src/ledger_app_clients/ethereum/client.py @@ -206,3 +206,68 @@ class EthAppClient: with self._send(chunk): pass return self._send(chunks[-1]) + + def set_plugin(self, + plugin_name: str, + contract_addr: bytes, + selector: bytes, + chain_id: int, + type_: int = 1, + version: int = 1, + key_id: int = 2, + algo_id: int = 1, + sig: Optional[bytes] = None): + if sig is None: + # Temporarily get a command with an empty signature to extract the payload and + # compute the signature on it + tmp = self._cmd_builder.set_plugin(type_, + version, + plugin_name, + contract_addr, + selector, + chain_id, + key_id, + algo_id, + bytes()) + # skip APDU header & empty sig + sig = sign_data(Key.SET_PLUGIN, tmp[5:-1]) + return self._send(self._cmd_builder.set_plugin(type_, + version, + plugin_name, + contract_addr, + selector, + chain_id, + key_id, + algo_id, + sig)) + + def provide_nft_metadata(self, + collection: str, + addr: bytes, + chain_id: int, + type_: int = 1, + version: int = 1, + key_id: int = 1, + algo_id: int = 1, + sig: Optional[bytes] = None): + if sig is None: + # Temporarily get a command with an empty signature to extract the payload and + # compute the signature on it + tmp = self._cmd_builder.provide_nft_information(type_, + version, + collection, + addr, + chain_id, + key_id, + algo_id, + bytes()) + # skip APDU header & empty sig + sig = sign_data(Key.NFT, tmp[5:-1]) + return self._send(self._cmd_builder.provide_nft_information(type_, + version, + collection, + addr, + chain_id, + key_id, + algo_id, + sig)) diff --git a/client/src/ledger_app_clients/ethereum/command_builder.py b/client/src/ledger_app_clients/ethereum/command_builder.py index a677671..e63f061 100644 --- a/client/src/ledger_app_clients/ethereum/command_builder.py +++ b/client/src/ledger_app_clients/ethereum/command_builder.py @@ -10,6 +10,8 @@ from .eip712 import EIP712FieldType class InsType(IntEnum): GET_PUBLIC_ADDR = 0x02 SIGN = 0x04 + PROVIDE_NFT_INFORMATION = 0x14 + SET_PLUGIN = 0x16 EIP712_SEND_STRUCT_DEF = 0x1a EIP712_SEND_STRUCT_IMPL = 0x1c EIP712_SEND_FILTERING = 0x1e @@ -219,3 +221,49 @@ class CommandBuilder: int(display), int(chaincode), payload) + + def set_plugin(self, + type_: int, + version: int, + plugin_name: str, + contract_addr: bytes, + selector: bytes, + chain_id: int, + key_id: int, + algo_id: int, + sig: bytes) -> bytes: + payload = bytearray() + payload.append(type_) + payload.append(version) + payload.append(len(plugin_name)) + payload += plugin_name.encode() + payload += contract_addr + payload += selector + payload += struct.pack(">Q", chain_id) + payload.append(key_id) + payload.append(algo_id) + payload.append(len(sig)) + payload += sig + return self._serialize(InsType.SET_PLUGIN, 0x00, 0x00, payload) + + def provide_nft_information(self, + type_: int, + version: int, + collection_name: str, + addr: bytes, + chain_id: int, + key_id: int, + algo_id: int, + sig: bytes): + payload = bytearray() + payload.append(type_) + payload.append(version) + payload.append(len(collection_name)) + payload += collection_name.encode() + payload += addr + payload += struct.pack(">Q", chain_id) + payload.append(key_id) + payload.append(algo_id) + payload.append(len(sig)) + payload += sig + return self._serialize(InsType.PROVIDE_NFT_INFORMATION, 0x00, 0x00, payload) diff --git a/client/src/ledger_app_clients/ethereum/utils.py b/client/src/ledger_app_clients/ethereum/utils.py new file mode 100644 index 0000000..aa3c710 --- /dev/null +++ b/client/src/ledger_app_clients/ethereum/utils.py @@ -0,0 +1,4 @@ +import sha3 + +def get_selector_from_function(fn: str) -> bytes: + return sha3.keccak_256(fn.encode()).digest()[0:4]