Postconditions on transactions

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.

What can be done? In theory, you could inspect the contract’s code before interacting with it, but who can reliably do that on any reasonable time frame? It’s also easy to obfuscate malicious behavior.

You could rely on trusted lists of safe websites and safe contracts, and those lists could be known to wallets, but this create enormous pressure on the list maintainers to verify and approve contracts.

You could try and simulate what the transaction will do, but a sophisticated attacker will create a contract with a switch to trigger the malicious behavior, and inject a transaction that turns on the switch right before your transaction. The simulation will make the contract appear innocuous, but it will still behave maliciously when the transaction is included in the block.

One solution to this conundrum is to enable postconditions on transactions. Interacting with a smart contract can trigger all sorts of side effects, but attackers primarily care about one: take your assets.

Every transaction in Tezos produces a receipt, which describes the changes to the ledger caused by the transaction. A post-condition would be a bit of Michelson code that is triggered after the transaction as completed and takes the receipt as input. The postcondition can then either succeed or fail. If it fails, the entire transaction is rolled back.

A typical postcondition would be to verify that none of the effects involve an allowance for an asset changing, or an asset changing hands. A dapp that does require an asset to change hand would propose to the wallets a postcondition that relaxes those conditions. The wallet would then be able to warn the user that the dapp has asked to authorize moving up to quantity qa and qb of assets A and B.

This can only be done with a protocol change, it’s not something you can build with just smart-contracts.

I think it would be a nice feature as people would be able to interact with smart-contracts without needing to trust their behavior as much.

This isn’t a silver bullet, there are state changes user care about that might not be caught in the “default” post-conditions. For example, if you have an NFT for sale in a marketplace, a malicious website could trick you into lowering the asking price a lot, and that wouldn’t look like an asset changing hands. It’s also something that would require a fair bit of standardization across wallets to be helpful.

Nonethless, it’s a unique feature that’s not available on any blockchain and that seems to solve a real pain point users are experiencing today.

17 Likes

Hey Arthur, with your proposed implementation, is it possible to have a transaction with a post condition, and for that post condition send another transaction with another post condition, etc?

If the above is possible, and the last post condition fails, do we roll back all the transactions triggered in all post conditions?

1 Like

That would be very useful - I’ve personally spent many hours vetting airdrops and the like to avoid the situation you described, wishing I had a mechanism like this.

One nit: You say this would require a change to the protocol, but why couldn’t it be done at the wallet level? Here, the wallet would just add a final op to the op batch that asserts nothing bad happened. IIUC, the switch from BFS → DFS contract execution means we’d know the assert-op would always be the last op in the batch, making it the postcondition you described.

1 Like

This seems related to Monitors.

3 Likes

No, the post condition’s only effect is to either allow or disallow the transaction. You could do what you describe but I think it would add a lot of complexity.

Because that transaction would have to iterate over all possible assets you might own and check their balance, it wouldn’t complete in time.

1 Like

You mentioned this in the recent AMA and I thought it was super interesting. Would be a fantastic addition to set Tezos apart and generate trust in the ecosystem. Not only for NFTs, but also DeFi.

This would be a great feature. How does it look in practice? A condition that says “no NFT asset will change hands” would have to check all assets in your wallet? But what does that look like for a user that holds thousands of NFTs in their wallet across a variety of standard (OBJKT) and non standard FA2 contracts?

re: transaction insight, would it not be possible (at wallet level) to show the user the contract interaction in broad strokes and it’s actions (“transfer X token to Y address”, “set approval for X tokens”)? Shouldn’t this be easier since Michelson is stack based and easily parsed?

No, the postconditions applies to the transaction receipt which would contain explicit information about NFT leaving your wallet. The fact that it’s not practical to iterate over every possible asset is exactly why this proposal requires a protocol change.

Why not doing an HTTP(S)-like for “SECURE” mechanism ?
Like secure for secure website under a specific certificate, we do trust some certificate authorities.

In Tezos, we could whitelist some audit companies that sign a particular smart contract, then we can consider it as “SECURE”. Wallet should be able to display a secure icon when this kind of contract is called, either displaying a warning when a smart contract has not been audited by a trusted audit company.
Wallets should manage a default list of audited company public keys, also being possible for the user to add new ones

@Benjamin_Fuentes
In Tezos, we could whitelist some audit companies that sign a particular smart contract, then we can consider it as “SECURE”

If a specific Tezos account chooses to trust a third party, that’s the business of that account (and by extension the account holder).

I wouldn’t want the protocol to automatically trust certain third parties by default as that will reduce decentralization and create risk, should any of those trusted parties be compromised.

Like I said, is owners’ wallet configuration, not the protocol

If I understand correctly, the post-condition is more lightweight to this secured-by-audit-companies mechanism. A security specifically required by certain scenario can be defined by a post-condition directly.

Lightweight?
It depends for who :slight_smile:

On protocol is just to add a digital signature from a public auditor

On wallet side is to manage a list of known and trusted auditors

So it is quite different from the post condition but aim to resolve the same troubles

Indeed, transaction monitors provide a solution. We have a paper (under review) with impossibility results (therefore the protocol must change) and recommendations about different ways to add capabilities for transaction monitoring. We will arxive and share the paper as soon as possible.

5 Likes

This idea is especially interesting to me in a future where events provide storage-independent representations of important information in the receipts …e.g. with new token standards. Until then I don’t see how there could be useful “default post-conditions” for tokens.

Maybe some kind of ticket-token convention – or even “no tickets move at all” – could also work as a default postcondition, but only for tickets.

Of course, there could also be default postconditions involving tez transfers.


I am confused about the example:

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.

Did you have any more detail in mind?

If the transaction goes to a malicious contract, it should not be able to steal my assets. If there is no malicious contract involved, it is not obvious why postconditions are useful, and many of your comments would not make sense.

So I guess we should imagine some kind of interaction between the asset contract and a malicious contract?


In general, I would like to think more about examples where postconditions could be useful, in order to better evaluate WIP designs. For example, one WIP design has postconditions declared on the contracts, and I don’t know what to think about that yet.

1 Like

@tom I’m sure not everyone on this thread is familiar with “storage-independent representations” (I’m not). Can you provide some examples?

Today, the only reliable representation of token balance changes in the receipts is the storage diff for the token contract. Since each token contract can use a different storage layout, you generally have to understand the details of the token contract to understand what the storage diff means.

Events seem helpful because if there is a “token event” standard, every contract implementing it will emit these events in the receipts, with the same representation everywhere, independent of each contract’s particular storage layout. So, a “storage-independent representation.”

Presumably a postcondition could then …somehow… scan through the receipts looking for these events.

2 Likes

My understanding of the problem here is a malicious website and not a malicious contract (at least for the example in question). A malicious website can create a valid transaction to an honest contract that goes against the wallet owner’s will. For example, a scam airdrop website can generate a transaction that transfers your tokens and the wallet owner would sign it thinking it is for the airdrop.

I am also interested in what attacks are possible via a malicious contract. I am guessing the attack surface would be smaller compared to malicious websites since, as you mentioned, a malicious contract itself cannot steal assets that exists in other contracts?

1 Like

Hi all, I have been thinking about the postcondition design, and it is unclear to me how the UX would work. Namely:

  • The fact that each contract will have a different storage layout makes it hard to have useful standard postconditions. For example, what would a general “won’t transfer tokens” postcondition look like exactly?
  • Without standard postconditions we would have to present raw Michelson postcondition lambdas. This would be confusing for average users.

(I’d be more than happy to be convinced otherwise, I do think postconditions is a cool idea).

With some thought, I propose pivoting to a simpler approach called transaction insights. The idea is to:

  1. Give smart contract writers the ability to attach “on-chain documentation”, which I call insights, to contracts. Such insight would be generated by a function with type parameter -> string
  2. Have the wallet present insights to users when asking for a signature.

For example, a transaction that transfers tokens would have an insight like “Transfering ownership of {nft_id} to {address}.” where {nft_id} and {address} is injected based on the parameter. Wallets can show such insight like this (hopefully in a more noticeable way):

Insights would prevent the “scam airdrop” attack since users would notice that the transaction he/she is about to sign is sending tokens to an unknown account.

Here is documentation with more details about the proposal: Transaction Insights - HackMD

Yes, insights would be less powerful than postconditions (most notably it doesn’t protect you from malicious contracts), but I personally think that it still protects against a large attack surface (i.e., malicious websites) with good UX. And good UX is important for security.

Any feedback/comments/questions will be greatly appreciated (either here or in the document). I am especially interested in things like:

  • Is there actually a way to make the postconditions UX work? If so, what would it look like?
  • What kind of concrete attacks can postconditions prevent and insights not prevent?
1 Like

Here is a quick report I wrote about the topic. It explains what kind of attacks are possible and what we can do to prevent them.

Here is a quote from the conclusion part:

It seems like some combination of Solution 1 (=contract white list) and Solution 2 (=better insight) will give us good security coverage. These will not require a protocol update but will require a lot of help from the wallet devs.
Postconditions is a cool idea, but we will need to find more use cases to justify the technical difficulties of implementing them.