Hans Hoglund
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Proposal for strengthening ticket handling in the Tezos Protocol **Status:** APPROVED **Authors:** Hans Hoglund, Joel Bjornson, Derek Sorensen **Target Protocol:** J **Summary:** Add runtime checking of ticket ownership, with no change to semantics and on-chain ticket representation. **Previous proposal:** https://hackmd.io/Gc2GZGV-QSuqHtzj2vvlsQ?view ## Background ### Motivation Tickets provide a first-class notion of ownership in the Tezos Protocol. In order to support use cases such as FTs, NFTs and permissions systems, tickets must satisfy strong ownership invariants. Though the current Protocol is believed to rule out invalid uses of tickets, we do not have certainty that it does. We therefore propose a simple mechanism to detect invalid ticket uses. The aim is not to *rule these events out in advance*, but to *detect them and signal an error* if they do. ### Ticket semantics We are not proposing any changes to ticket semantics, but here is a brief overview of the current state: Tickets have *value semantics*. Tickets that have the same creator, value and amount can not be distinguished. A contract can create any number of tickets with `TICKET`. The creator and value fields are immutable, and can not be changed by `SPLIT_TICKET` and `JOIN_TICKET`. The only way for a contract to retrieve a ticket it has not created itself, is to have it *transfered* from another contract, via `CREATE_CONTRACT` or `TRANSFER_TOKENS`. #### Token types We can think of a pair of a creator (a contract address) and a value (a packable value) as a *type of token*. For example `(Alice, "red")`, `(Alice, "blue")` and `(Bob, "red")` are all distinct *token types*. Token types are not the same as Michelson types. For example `(Alice, False)` is a token type, and corresponding tickets have the (Michelson) type `ticket bool`. Possessing (in storage or on stack) a ticket `(creator, value, n)` implies ownership of `n` tokens of token-type `(owner, value)`. For example, possession of the ticket `(Alice, "red", 5)` implies ownership of 5 `(Alice, "red")` tokens. > Note: When we refer to *tokens* in this document, we mean the kind of tokens implied by tickets, not native tokens or smart-contract encoded tokens. ### Guarantees from the type system The Michelson type system aims to prevent falsification and bad ownership change, using the Duplicable attribute. Basic types such as `nat` are Duplicable. Tickets are not Duplicable by design. Duplicability of containers [depends on what they contain]([src/proto_alpha/lib_protocol/script_ir_translator.ml](https://gitlab.com/tezos/tezos/-/blob/master/src/proto_alpha/lib_protocol/script_ir_translator.ml#L996)). `list T` is Duplicable iff T is Duplicable, and so on. In Haskell we would write: ```hs instance Duplicable a => Duplicable (List a) instance Duplicable v => Duplicable (BigMap k v) ... ``` The `DUP` operation is forbidden on non-Duplicable values. This is checked statically (at Michelson type-checking time). This guarantees that a ticket cannot be sent to some other contract and, at the same time, remain in the sender's storage. ### Guarantees from runtime checks Most guarantees about ticket ownership come from the Michelson type system and calling convention. There are also a few runtime checks: - `operation` is a Duplicable type for legacy reasons. However, at most one out of two operations resulting from a DUP call can be used, or execution will fail. - Implicit accounts are prevented from sending ticket-containing types to contracts. These mechanisms prevent tickets from being serialized (and copied) or forged by instantiating them as storage when originating a new contract. ### Confidence We are interested in ruling out invariants such as: - Contracts forging tickets. - Implicit accounts forging tickets. - Accidental duplication of tickets. Although the current implementation of tickets is believed to rule these out, as described above, this has not been formally proven. Furthermore, changes to the Protocol codebase such as extending Michelson with new instructions may introduce new vulnerabilities. ## Proposal We propose to decorate the Protocol with a optional *token validation* layer, which tracks balances per contract and token type. The only purpose of the validation layer is to reject invalid operations. In addition to rejecting the block containing the invalid transaction, validation errors can be logged and automatically reported to Protocol developers. **Token validation failure should always be treated as a Protocol bug**. Compared with [our previous proposal](https://hackmd.io/Gc2GZGV-QSuqHtzj2vvlsQ?view), this does not change the representation of tickets. It only adds an extra layer of protection to detect invalid operations. ### Ticket-balance table The validation layer consists of: - A new storage table `Balance` - Functions to update the `Balance` table and fail on invalid states The storage table has the following layout, mapping *ticket creator*, *content* and *current owner* to an amount. In other words, it gives the current balance for a given *contract* and *token type*. The table is sparse: a missing row means a balance of zero. | Creator x Content x Owner | Amount | |---------------------------|-----------| | (KT789, "T1", KT789) | 1 | | (KT789, "T1", KT123) | 5 | | (KT700, Unit, KT800) | 7 | | ... | ... | To ensure fast lookups, the content should be a type supporting hashing and equality. Because ticket content is always comparable, either these exist or they can be implemented (or the underlying Merkle hash could be used). The Michelson ticket instructions are **not** impacted and do not perform any additional side effects. Instead, validation is performed in pre and post-execution steps. ### An example Below is an example of how to account for tickets in a contract call to $K_1$. #### Pre-execution All $K_1$'s tickets exist in either: - The *incoming* arguments passed to $K_1$, which we call $I$. - $K_1$'s storage. We call the number of tokens in the storage $S_1$. ```graphviz digraph { compound=true rankdir=TB graph [ fontname="Source Sans Pro", fontsize=20 ]; node [ fontname="Source Sans Pro", fontsize=18]; edge [ fontname="Source Sans Pro", fontsize=12 ]; { concentrate=false ts [label="K1's Tickets"] [shape=box] i [label="Parameters (I)"] [shape=box] s [label="Storage (S1)"] [shape=box] lz [label="Lazy"] [shape=box] ss [label="Strict"] [shape=box] ts -> s ts -> i s -> ss s -> lz label="Pre-execution" } } ``` We trust that the balance table has been updated to reflect the accounting of these tickets in a previous post-execution step. We refer to the balance table projection for $K_1$ as $B_1$. The balance, $B_1$, for some token before execution is: $$ B_1 = S_1 + I $$ For example, assume $K_1$ receives one $Alice^{Blue}$ token in parameters, and has four of them in storage, along with one $Bob^{Red}$ and one $K_1^{Yellow}$. We have: | Token type | $S_1$ | $I$ | $B_1$ | |----------------|-------|--------|------:| | $Alice^{Blue}$ | 4 | 1 | 5 | | $Bob^{Red}$ | 1 | 0 | 1 | | $K_1^{Yellow}$ | 1 | 0 | 1 | #### Execution Michelson is executed, producing operations and *new storage*. This has no effect on the balance table. #### Post-execution After executing contract $K_1$, tickets may exist in $K_1$'s storage as well as in emitted operations: ```graphviz digraph { compound=true rankdir=TB graph [ fontname="Source Sans Pro", fontsize=20 ]; node [ fontname="Source Sans Pro", fontsize=18]; edge [ fontname="Source Sans Pro", fontsize=12 ]; { concentrate=false ts [label="Tickets After"] [shape=box] os [label="Operations (outgoing)"] [shape=box] s [label="Storage (S2)"] [shape=box] lz [label="Lazy"] [shape=box] ss [label="Strict"] [shape=box] ts -> s ts -> os s -> ss s -> lz label="Pre-execution" } } ``` All $K_1$s tickets (except for any tickets in parameters in calls to $K_1$) now only exist in $K_1$'s storage. We perform a diff, $\Delta$, on the old and new balances for a token type: $$ \Delta = B_2 - B_1 = S_2 - B_1 = S_2 - S_1 - I $$ :::info Calculating $(\Delta = S_2 - S_1 - I)$ naively may be expensive and we we would like to avoid performing a strict traversal over both storage values. See the [implementation section](#Implementation) below for details. ::: The diff may tell us that $K_1$ has withdrawn two $Alice^{Blue}$ and one $Bob^{Red}$ from the store, and deposited one $K_1^{Yellow}$. The new balance $B_2$ is equivalent to what the amount put back into $K_1$'s storage $S_2$. | Token type | $B_1$ | $I$ | $S_1$ | $S_2$ | $\Delta$ |----------------|-------|-------|-------|-----------|-------:| | $Alice^{Blue}$ | 5 | 1 | 4 | 2 | -3 | | $Bob^{Red}$ | 1 | 0 | 1 | 0 | -1 | | $K_1^{Yellow}$ | 0 | 0 | 0 | 1 | 1 | We now verify that for each row, either: 1. Balance did not increase ($\Delta <= 0$), or 2. This token type is of the shape $(K_1, \_)$, that is $K_1$ created tokens in its own namespace. Assuming no errors, the balance table for $K_1$ can be updated according to the new balances. We then proceed to update balance for recipients. The diff table above implies a *spending budget* for each token type, not created by $K_1$. The budget for tickets created by $K_1$ is unlimited as $K_1$ could have minted any number of them. Formally, for some set of minted tokens $t_{mt}$, outgoing tokens $I_o$ must satisfy: $$ I_o <= B_1 - B_2 + I_{mt} $$ If $I_o$ is smaller than the RHS then at least one ticket was *dropped*. We compute $I_o$ by a strict deep traversal of the Michelson operations (rejecting duplicated operations), finding all *outgoing* tickets from the operations parameters in either `CREATE_CONTRACT` or `TRANSFER_TOKEN`, resulting in a table such as: | Token | Recipient | Amount | |----------------|------------------|-------------------:| | $Alice^{Blue}$ | $K_2$ | 1 | | $Alice^{Blue}$ | $K_3$ | 2 | | $K_1^{Green}$ | $K_2$ | 10 | First, we validate that the total outgoing amounts of each ticket type does not exceed the *spending budget*. If the budget is exceeded — implying that a ticket has **somehow** been forged — an error is thrown and the transaction fails. In the case above, we have 3 outgoing units of $Alice^{Blue}$ tickets. This is compatible with the budget. We also have 10 units of $K_1^{Green}$ to $K_2$ which is fine, as $K_1$ tickets don't have any spending budget. The balance table can now be updated for the $K_2$ and $K_3$ contracts. The projection of the transfer table for each recipient contract becomes incoming tokens $I$ in the next post-execution phase. #### Summary The call to contract $K_1$: - Transferred 3 units of $Alice^{Blue}$ to other contracts. - Created 1 unit of $K_1^{Yellow}$ and saved in the storage. - Created and transferred 10 units of $K_1^{Green}$ to another contract. - Dropped 1 unit of $Bob^{Red}$. #### Implementation Any value, $S$, representing the number of tokens in the storage can be split into *strict* and a *lazy* parts, so that: $$ S = S^{strict} + S^{lazy} $$ The strict part consists of tickets in any non-lazy data structure (such as `pair` and `list`), while the `lazy` part is the number of tickets in lazy structures, or more specifically `big-map`s. Expanding the definitions for $B_1$ above using the strict and lazy partitioning scheme gives: $$ \begin{align} B_1 &= S_1 + I = S_{1}^{strict} + S_1^{lazy} + I \implies \\ S_1^{lazy} &= B_1 - S_{1}^{strict} - I \end{align} $$ We can also expand the definition for the new balance, $B2$: $$ \begin{align} B_2 = S_2 = S_2^{strict} + S_2^{lazy} \end{align} $$ When executing a Michelson script, we're also give a *lazy-diff* value that represents changes to the lazy part of the storage. Introducing a term $S_{\delta}^{lazy}$ for the part of the diff that concerns tickets with ticket count $S$, implies: $$ S_2^{lazy} = S_1^{lazy} + S_{\delta}^{lazy} $$ After plugging in the equation for $S_1^{lazy}$: $$ S_2^{lazy} = (B_1 - S_1^{strict} - I) + S_{\delta}^{lazy} $$ We thus have the following formula for $B_2$: $$ B_2 = B_1 + S_2^{strict} + S_{\delta}^{lazy} - S_{1}^{strict} - I $$ Or: $$ B_2 = B_1 + \Delta $$ Where: $$ \Delta = S_2^{strict} - S_{1}^{strict} + S_{\delta}^{lazy} - I $$ So, in order to calculate the new balance $B_2$ and $\Delta$ for some token, we need: - $B_1$ — balance before the execution of the contract. - $S_1^{strict}$ — the amount of tokens in the strict part of the old storage. - $S_2^{strict}$ — the amount of tokens in the strict part of the new storage. - $S_{\delta}^{lazy}$ — the amount of tokens *added/removed* to the lazy part of the storage. - $I$ — the amount of tokens contained in the incoming arguments. #### Tickets inside lazy structures (big-maps) The scenarios for which we need to traverse both the strict and the lazy parts of a data-structure are: 1) Tickets contained a big-map in the incoming parameters. 2) Tickets contained in a big-map in some outgoing parameters to `TRANSFER_TOKEN` operation. 3) Tickets contained in a big-map in the new storage of a `CREATE_CONTRACT` operation. Note that if a big-map does not contain tickets, it will never be traversed. Additional scenarios for when values of big-maps are traversed: 4) When a big map containing tickets changes ownership—by being sent as parameter from a contract to another or to the storage of a new contract. 6) A big-map containing tickets is dropped. 7) A big-map containing tickets is moved within the storage. All these scenarios require traversing the list of elements in the big-map and hence is linear in the size. Given these constraints, putting tickets in big-maps that are either moved or destroyed is not recommended. ### Other rejected scenarios Let us now consider how various invalid scenarios are rejected by the token validation layer. We use the word **somehow** to refer to an unexpected bug in the Protocol (to be fixed and corrected when flagged by the token validation layer). #### Contracts forging tickets A contract **somehow** has on its stack a ticket implying ownership of a token it did not recieve or originate. Any attempt to transfer or store this ticket will fail, because no corresponding record exists in the balance table. #### Implicit account forging a ticket A user **somehow** is allowed to originate a smart contract with a ticket in its storage type. Any subsequent attempt to transfer this token will fail, because no corresponding entry exists in the balance table. #### Accidental duplication A contract **somehow** duplicates a ticket. If the contract attempts to transfer both tickets, the first operation will succeed (and update the balance table), and the second one will fail as the balance is now too low. ## Pre-populating the balance table Tickets created before adoption of this Protocol will not have corresponding entries in the balance table, so these have to be created retroactively. We propose that migration to this Protocol traverses the state of all contracts by contracts whose storage type contains tickets (estimated to be <1% of mainnet contracts) adding entries corresponding to the tickets in their storage. The implementation of this proposal will include evidence that this migration (stitch) completes in reasonable time on mainnet. ## Alternative approaches ### Formal verification Instead of tracking token ownership via runtime validation, we could attempt to formally prove that the Protocol rules out the above failure cases for all possible ledger states. In order to prevent breakage on further protocol changes, the verification would have to be applied to future versions of the code base as well. In practice this could mean: - Running `coq-of-ocaml` applied to the current Protocol code base in the CI, or: - Using `mi-cho-coq` and prove that it is equivalent to the current Protocol codebases. Our proposal is lightweight in that it does not change protocol semantics, and does not rule out future attempts at verifying correctness (though a successful proof of correctness would render the checks suggested here unnecessary). ## Related work - Document the exact semantics of tickets better. - [Fix the documentation](https://gitlab.com/tezos/michelson-reference/-/issues/51) of Duplicable and other attributes. - Add more regression tests to verify the behaviour of Michelson attributes (especially Duplicable).

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully