owned this note
owned this note
Published
Linked with GitHub
# Transaction Insights
## Problem
Consider this scenario (quote from [here](https://forum.tezosagora.org/t/postconditions-on-transactions/4516?u=linoscope)):
> You follow a link to a website on an influencer’s social media channel. The site promises to airdrop some tokens if you own a certain NFT. You pair your wallet with the dapp, sign a transaction to claim your airdrop and … all the assets in your wallet are gone. The influencer’s social media account was hacked, the link sent you to a malicious website and you signed away your assets when doing the transaction.
Here is a breakdown of what is happening:
1. You visit a website that promises to airdrop some tokens if you own a certain NFT
2. Your wallet prompts you to confirm a transaction like below. Note that it just says "interaction" together with a contract address.
* ![](https://i.imgur.com/8p3LKGw.png =250x)
4. You assume that the transaction is for the airdrop and press "confirm" without looking closely at the operation.
5. The transaction you just signed was actually a "transfer" call to an NFT contract! Your precious NFT is now stolen.
The fundamental problem here is that you, the wallet owner, **do not know what you are signing when you are confirming a transaction** (a problem called [*blind signing*](https://cointelegraph.com/news/ledger-cto-warns-crypto-users-about-the-dangers-of-blind-signing)).You CAN check the raw operation JSON (see below for example) and inspect that it is not malicious, but it is unrealistic to expect every user to do that for every transaction.
<details>
<summary>(Example of a raw operation JSON)</summary>
{
"branch": "BLJTxJCSJU88fKxUrG9khgLmVAmW9CzUAowVvX7o73kJ3j5fQb9",
"contents": [
{
"kind": "transaction",
"source": "tz1dZCh7fPh2VdqhrQevURAW48h5yyFeh4qc",
"fee": "1032",
"counter": "69709908",
"gas_limit": "6273",
"storage_limit": "350",
"amount": "0",
"destination": "KT1SH6c1iXuqX5U8QiJmhe9jnjcibdTodJNC",
"parameters": {
"entrypoint": "transfer",
"value": [
{
"prim": "Pair",
"args": [
{
"string": "tz1dZCh7fPh2VdqhrQevURAW48h5yyFeh4qc"
},
[
{
"prim": "Pair",
"args": [
{
"string": "tz1LF2ZAwqrHQPyqpfhrUEvtyoM46mgbSrgx"
},
{
"prim": "Pair",
"args": [
{
"int": "2"
},
{
"int": "1"
}
]
}
]
}
]
]
}
]
}
}
]
}
</details>
## Solution
(Note: the following is the initial design and is highly likely to change. Namely, we probably do not need a new top-level keyword and just define a spec for a view although we will probably need a protocol update that enables stringifying `address`, `int`, etc. Will update the doc later.)
Associate "on-chain documentation", which we call *insights*, to contract entrypoints. Such insights will be prompted to users to better inform them on what they are signing.
Namely, we introduce a new `insight` top-level keyword:
```
insight <lambda>
```
The type of `<lambda>` should (conceptually) be like below where `parameter` is the contract parameter and `string` is the insight.
```ocaml
parameter -> string
```
For example, a "transfer" endpoint of an NFT contract will generate an insight like:
```
Transfering ownership of {nft_id} to {address}.
```
where `{nft_id}` and `{address}` is filled based on the parameter.
Wallets will call some API (like `<contract-id>/gen_insight`) with specific parameters which internally calls the above insight lambda to obtain insight. Based on this, the wallet will show something like:
```
Message from contract: Transfering ownership of {nft_id} to {address}.
```
![](https://i.imgur.com/OALc3zD.png =250x)
Note that with "Message from contract" string we highlight that the insight is from the contract and not from the wallet. This is important as users must know that a malicious contract can lie in the insight.
## Security
What does transaction insights protect against?
* A malicious website that generates transaction to an honest contract in a way that goes against the wallet owner’s will.
* In the airdrop example, the wallet owner will notice that something is wrong when the wallet shows "Transfering ownership of ... to {address}" where `{addreess}` is an unfamiliar address.
What does this NOT protect against?
* A malicious contract that lies in the insight.
* But note that what a malicious contract can do is pretty limiting (I think). For example, it cannot steal NFT assets that live in another contract.
* An honest contract that has a bug.
* E.g. a transaction can say it doesn't transfer tokens but actually does because of a bug.
## Comparison with transaction postconditions
Ref: [Post conditions design doc](https://hackmd.io/@marigold-dev/B1QfwUli9)
* Pros of insights:
* Better UX for end users - we don't need to present raw lambda code like we do in postconditions.
* The UX of postconditions would be much better if we can have a fixed set of postconditions and associate human readable strings (like "Won't transfer tokens") to them. But I think that would be hard considering that each contract will have different storage layout (maybe tickets would solve this?) making it hard to have a general "won't transfer tokens" postcondition.
* Gives the same level of security for the case when:
* The website is malicious, and
* The contract is honest and doesn't have a critical bug.
* (And I belive this is the largest attack suface?)
* No additional gas for contract calls since the insight generation will be done via API call and not while executing the transaction.
* Pros of postconditions:
* Post conditions provide "on chain truth" of what will happen. We do not need to trust anyone - the Michelson postcondition code is the truth.
* Can protect against malicious contracts.
* Can protect against bugs in the honest contract.
It is worth mentioning that the utility of postconditions will increase a lot (at least for the usecases related to tokens) once either/both:
* Implicit accounts can own tickets AND if ticket transfer is encoded in the reciept AND if tickets are widely used to represent tokens
* We have standardized "on-chain events" that will be emitted when there is a token transfer.
## Other alternatives
### Just display the function name and Micheline parameter
This is currently done in the Kukai wallet:
![](https://i.imgur.com/t6Df9os.png =250x)
Pros:
* Doesn't require changes, both in protocol and in smart contracts
* Malicious smart contracts cannot lie about function name and parameter
Cons:
* Hard to read for non-tech people.
* Especially problematic for Tezos since the argument tend to be complex ADTs.
* Malicious smart contracts can construct deceptive arguments (e.g. take an "reciever" address parameter but not use it) anyways
### Preload wallets with special messaging logic for popular contracts
Probably (have not confirmed) how Temple wallet displays "1 OBJKT" string when transfering OBJKTs:
![](https://i.imgur.com/2UrzksW.png)
Pros:
* Doesn't require changes, both in protocol and in smart contracts
Cons:
* Not scalable
### TZIP16 metadata
TBF. Notes:
* Sounds like extending TZIP16 makes more sense than to add an `insight` construct.
* We will still probably need a protocol change to extend Michelson with a STRINGIFY instruction to be able to generate insights for non-standard contracts though.
## Open questions
* How important is it to protect against malicious/buggy contracts?
* What concrete attack is possible with insights that are impossible with postcondidtions?
* Can we do "insights" outside of the protocol?
* Have a central "documentation provider" node. The provider will maintain a contract+endpoint to documentation mapping. You will need to provide a signature to update the mapping.
* Does seem more complex?
* Categorize the insights into `INFO` and `WARN` so we can `WARN` level insights stand out more (popups, red font, etc.)?
* Transfering NFTs should be `WARN` level.
* Not sure what would be `INFO` level?
* Somehow highlight unknown address strings in the insight?
* Anyway to attach insights to existing contracts?
* Internationalization. Hard-coding english would make it hard.
* In order to have multiple languages in the contract level we will need utf8 support in Michelson
* What do we do for "batch" transactions?
* ![](https://hackmd.io/_uploads/SyZXPKAhq.png =200x)
## Notes
* (Tom Jack) it's not clear why a protocol amendment is needed -- there could be a convention for doing "insights" in metadata or in views for example?
* (Thomas) Agree: If insight are only "declarative", contract editors can already create onchain views that do the same; so it is just about standardisation (maybe tzip10 amendement). Same would be done with metadata (tzip16) they are not very used but it works for existing contract that managed editable metadatas
* (Tom Jack) Michelson lacks primitives for converting things to strings (e.g. addresses, numbers) because you aren't expected to do such things
* (Lin) For on-chain insights to work, sounds like we would need to expand Michelson and add a a stringify opcode and "Stringifyable" attributes on a bunch of value types? (Not sure if it would be worth it at this point, but as an option)
* (Tom Jack) the Kukai thing seems a bit unfair -- Kukai is doing the wrong thing, but, they could do something better, because a parameter type will likely have annotations
* (Thomas Haessle) While for insight you are still expecting that the user will not "blind sign" with meaningful text. All experience about "legal terms", "privacy info" on web show that most are still bling validating.
* (Lin) Yeah, the nice thing about postconditions is that, if done correctly, they protect users "on chain" without trust. It's just that I am failing to see how we can do it correctly... Hopefully showing alarmful noticable messages for dangerous insights would help, but yes, some % of users will just click "confirm" anyways
* (Thomas Haessle) What happens when there is a FAILWITH in the insight?
## Related work
https://support.keyst.one/advanced-features/decode-defi-transactions
https://blog.keyst.one/why-are-hardware-wallets-out-dated-for-defi-8a3d81a27572
https://metamask.zendesk.com/hc/en-us/articles/4412543412123