# Ledger support for generic deploys
## Problem description
Casper Ledger app already supports 3 types of deploys:
* native transfer
* delegate
* undelegate
All three have special handling implemented in the Ledger application so that they're presented in a simple and understanble form to the users. Other deploy types (that are not one of the three) are being rejected. (How the native transfer is presented in the Ledger can be found under https://docs.casperlabs.io/en/latest/workflow/ledger-setup.html).
Deploy object is composed of multiple features:
* contract to execute
* contract arguments
* context account
* ttl
* timestamp
* dependencies
* approvals
* etc.
All elements but the first two are non-controversial and easy to represent in the Ledger (one page to confirm per element). It's the **contract to execute** and **contract arguments** that are problematic.
Casperlabs deploy can be one of 6 types (**contract to execute**):
1. `ModuleBytes(bytes)` - `bytes` can be arbitrary program to execute
2. `StoredContractByHash(hash)` - `hash` is an address of a previously stored contract
3. `StoredContractByName(name)` - `name` is an entry in the named keys of the account's context.
4. `StoredVersionedByHash(hash, version)` - a contract stored under `hash` but of specific `version`.
5. `StoredVersionedByName(name, version)` - a contract under `name` in the named keys but of specific `version`.
6. ~~`Transfer` - not important for the problem; has special handling in the Ledger already.~~
**Runtime arguments** can:
* have arbitrary number of elements (it's up to the creator of the contract to define it)
* have arbitrary `CLType` (again, up to the creator of the contract). `CLType` can also be recursive - like a `List` or `Map` which can contain other `CLType`.
In order to support all deploy types (generic transactions), we have to decide how to represent them in the Ledger. Unfortunately, the flexibility of our deploy structure works against us here - we need to decide on the presentation layer that is:
1. easy to understand and verify
2. flexible enough to accomodate all txn types
3. is not overwhelming to the user
## Proposed solution
The solution I'm proposing here is to solve for **1)** first and foremost, then for **2)**.
For displaying deploy types (**contract to execute**):
| Type | Ledger Page 1 | Ledger Page 2| Ledger Page 3 | Ledger Page 4 |
| -------- | -------- | -------- | -------- | -------- |
| `ModuleBytes(bytes)` | **Type:** Execute Contract | **Body hash:** <hash of bytes> | n/a | n/a |
| `StoredContractByHash(hash)` | **Type:** Execute Contract | **Address:** <hash> | **Entrypoint:** <entrypoint> | n/a |
| `StoredContractByName(name)` | **Type:** Execute Contract | **Name:** <name-utf8-encoded> | **Entrypoint:** <entrypoint> | n/a |
| `StoredVersionedByHash(hash, version)` | **Type:** Execute Contract | **Address:** <hash> | **Entrypoint:** <entrypoint> | **Version:** <version> |
| `StoredVersionedByName(name, version)` |**Type:** Execute Contract | **Name:** <name-utf8-encoded> | **Entrypoint:** <entrypoint> | **Version:** <version> |
For **runtime arguments**, we choose to use at least two Ledger pages per argument:
| n | Ledger element 1 | Ledger element 2 |
| -------- | -------- | -------- |
| `arg-0` | **Arg-0-name:** <name-of-arg> | **Arg-0-val:** <value-of-arg> |
| `arg-1` | **Arg-1-name:** <name-of-arg> | **Arg-1-val:** <value-of-arg> |
| `arg-2` | **Arg-2-name:** <name-of-arg> | **Arg-2-val:** <value-of-arg> |
| ... | ... | ... |
For each `arg-n-val` argument, we parse it to a `String` representation and display it on possibly multiple pages:
| CLType | Ledger parsed | Example |
| -------- | -------- | -------- |
| `bool` | true/false | `true`
| `number` (i32/64/..., u32/64/../512) | number | `1`
| `String` | utf-8 encoded string | `deaf beat`
| `Option:Some(cl_value)` | Ledger parsed `cl_value` | `Some("deaf beat")`
| `Option:None` | `None`, literally | `None`
| `Map(key1:val1, key2:val2, ...)` | `{key1:val1, key2:val2, ...}` where both `key`s and `val`s are Ledger parsed (possibly recursive) |
| `Result:Ok(cl_value)` | `Ok(cl_value)`, Ledger parsed `cl_value` |
| `Result:Err(cl_value)` | `Err(cl_value)`, Ledger parsed `cl_value` |
| `Tuple1(cl_value)` | `[cl_value]`, Ledger parsed | `(Ok([1,2,3]))`
| `Tuple2(cl_value1, cl_value2)` | `(cl_value1, cl_value2)`, Ledger parsed | `[10, [keyA: [1]]]`
| `Tuple3(cl_value1, cl_value2, cl_value3)` | `(cl_value1, cl_value2, cl_value3)`, Ledger parsed | `[10, [keyA: [1]]]`
| `List<CLType>` | `[el1, el2, el3, ...]`, Ledger parsed | `[1, 2, 3]`
| `Unit` | `unit`, literally | `"unit"`
| `PublicKey` | |
| `URef` | |
| `ByteArray[u32]` | |
| `Any` | |
| `Key` | |
### Simplified Runtime Arguments representation
In order to keep the transaction signing somewhat mangeable, we need to make some compromises about what can be displayed in the Ledger. We choose to:
1. Accept only deploys with fewer than 10 arguments.
2. Accept limited combinations of collections and containers.
3. Restrict collection sizes to 10 elements.
#### Allowed
##### Primitive types:
| Type | Example | Notes |
| -------- | -------- | -------- |
| `bool` | `true`/`false` | n/a |
| `i32` | -2^32^-1 .. 2^32^-1 | n/a |
| `u8`/`u32`/`u64`/`u128`/`u256`/`u512` | Text | `u512` spans 7 page on Ledger! |
| `Unit` | `Unit` | Literally, `Unit` |
| `String` | `dead beef` | UTF-8 encoded String |
| `ByteArray[u32]` | `ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` | hexencoded bytes |
| `Any` | Text | Text |
| `PublicKey` | `public-key-<hexencoded_publickey>` | n/a |
| `URef` | `uref-<hexencoded_uref_bytes>` | n/a |
| `Key` | `<key_prefix>-<hexencoded_key_bytes>` | n/a |
##### Containers
| Type | Example | Notes |
| -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------- |
| `Option<Primitive>` | `Some(primitive)`, `None` | n/a |
| `Result<PrimitiveA, PrimitiveB>` | `Ok(primitive)`, `Err(primitive)` | n/a |
| `Tuple1<Primitive>` | `(primitive)` | We use parenthesis for tuples |
| `Tuple2<PrimitiveA, PrimitiveB>` | `(primitive, primitive)` | n/a |
| `Tuple3<PrimitiveA, PrimitiveB, PrimitiveC>` | `(primitive, primitive, primitive)` | n/a |
| `Option<List<Primitive>>` | `Some([1,2,3,...])`, `None` | Inner list limited to 10 elements. |
| `Option<Map<Primitive>>` | `None`, `Some({keyA:valA, keyB:valB, ...})` | Inner map limited to 10 elements. |
| `Result<Option<Primitive>, Option<Primitive>>` | `Ok(None)`, `Ok(Err(primitive))`, `Err(None)`, `Err(Ok(primitive))` | n/a |
| **???** `Tuple1<Option<PrimitiveA>>` | `(Some(primitive))`, `(None)` | Is it usefull? |
| `Tuple2<Option<PrimitiveA>, Option<PrimitiveB>>` | `(Some(primitive), None)`, `(None, None)`, `(None, Some(...)`, `(Some(...), Some(...))` | n/a |
| `Tuple3<Option<PrimitiveA>, Option<PrimitiveB>, Option<PrimitiveC>>` | ... | As above but for `Tuple3` |
| **???** `Tuple1<Result<Primitive, Primitive>>` | `(Ok(primitive))`, `(Err(primitive))` | Is it usefull? |
| `Tuple2<Result<Primitive, Primitive>, Result<Primitive, Primitive>>` | `(Ok(primitive), Err(primitive))`, `(Err(primitive), Err(primitive))`, etc. | And other combinations |
| `Tuple3<Result<Primitive, Primitive>, Result<Primitive, Primitive>, Result<Primitive, Primitive>>` | `(Ok(primitive), Err(primitive), Ok(primitive)`, etc. | as above but for `Tuple3` |
##### Collections
| Type | Examples | Comment |
| -------- | -------- | -------- |
| `List<Primitive>` | Text | Up to 10 elements in collection |
| `Map<PrimitiveA, PrimitiveB>` | Text | Up to 10 elements in collection |
| `List<Option<Primitive>>` | Text | Up to 10 elements in collection |
| ??? `List<Result<Primitive>>` | Text |**TBD:** Weird type, not sure if usefull; <br>Up to 10 elements in collection |
### Not allowed
All others basically, including:
| Type | Example | Note |
| ------------------------------- | --------------------------------------- | ---- |
| `Option<Option<...>>` | `Some(None)`, `Some(Some(...))`, `None` | n/a |
| `Result<Result<...>>` | n/a | n/a |
| `Map<ContainerA, ContainerB>` | n/a | Container here is `Result`, `Option` or `TupleN` |
| `Map<CollectionA, CollectionB>` | n/a | n/a |
| `List<Collection>` | `List<List<...>>`, `List<Map<...>>` | n/a |
| `List<TupleN>` | n/a | n/a |
| `TupleN<TupleN>` | n/a | n/a |
| `TupleN<Collection>` | n/a | n/a |
### Questions
1. **Can we count, upfront, how many "clicks" will be required to validate the transaction?** Some types, like primitive `U512::MAX`, occupy 7 Ledger pages ( (require 7 clicks to verify the value) so a deploy with limited *Runtime Arguments* and limited elements in the collection can still require 100+ clicks: a `Map<U512, U512>` where we need 14 pages per element - with 10 elements in the map that's 10 x 14 = 140.
**Answer:** After consulting with Zondax developers - yes we can.
2. **Can we sign just a transaction hash?** For cases when Ledger chooses to not present the transaction's details to the user (either too many runtime arguments or too many elements or unsupported type combination), we could ask him/her to switch to **Expert mode** and sign just transaction hash.
**Answer:** After consulting with Zondax developers - yes we can.
## Downsides
The obvious downside of this approach is its verbosity - since we're using at least two pages per argument (though, probably more since each Ledger page has limited characters so longer strings are split up and displayed on multiple pages), it might potentially require multiple "clicks" before accepting/signing a deploy with Ledger.
## Alternatives