"access function" -> "main function"

This commit is contained in:
Christian Rinderknecht 2020-02-21 18:13:09 +01:00
parent f4d688df7b
commit feaf143209
6 changed files with 53 additions and 54 deletions

View File

@ -1,24 +1,24 @@
--- ---
id: entrypoints-contracts id: entrypoints-contracts
title: Access function and Entrypoints title: Main function and Entrypoints
--- ---
## Access Functions ## Access Functions
A LIGO contract is made of a series of constant and function A LIGO contract is made of a series of constant and function
declarations. Only functions having a special type can be called when declarations. Only functions having a special type can be called when
the contract is activated: we called them *access functions*. An the contract is activated: we called them *main functions*. A main
access function takes two parameters, the *contract parameter* and the function takes two parameters, the *contract parameter* and the
*on-chain storage*, and returns a pair made of a *list of operations* *on-chain storage*, and returns a pair made of a *list of operations*
and a (new) storage. and a (new) storage.
When the contract is originated, the initial value of the storage is When the contract is originated, the initial value of the storage is
provided. When an access function is later called, only the parameter provided. When a main function is later called, only the parameter is
is provided, but the type of an access function contains both. provided, but the type of a main function contains both.
The type of the contract parameter and the storage are up to the The type of the contract parameter and the storage are up to the
contract designer, but the type for list operations is not. The return contract designer, but the type for list operations is not. The return
type of an access function is as follows, assuming that the type type of a main function is as follows, assuming that the type
`storage` has been defined elsewhere. (Note that you can use any type `storage` has been defined elsewhere. (Note that you can use any type
with any name for the storage.) with any name for the storage.)
@ -42,13 +42,12 @@ type return = (list (operation), storage);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
The contract storage can only be modified by activating an access The contract storage can only be modified by activating a main
function. It is important to understand what that means. What it does function. It is important to understand what that means. What it does
*not* mean is that some global variable holding the storage is *not* mean is that some global variable holding the storage is
modified by the access function. Instead, what it *does* mean is that, modified by the main function. Instead, what it *does* mean is that,
given the state of the storage *on-chain*, an access function given the state of the storage *on-chain*, a main function specifies
specifies how to create another state for it, depending on a how to create another state for it, depending on a parameter.
parameter.
Here is an example where the storage is a single natural number that Here is an example where the storage is a single natural number that
is updated by the parameter. is updated by the parameter.
@ -89,17 +88,18 @@ let main = ((action, store): (parameter, storage)) : return =>
## Entrypoints ## Entrypoints
In LIGO, the design pattern is to have *one* access function that In LIGO, the design pattern is to have *one* main function called
dispatches the control flow according to its parameter. Those `main`, that dispatches the control flow according to its
functions called for those actions are called *entrypoints*. parameter. Those functions called for those actions are called
*entrypoints*.
As an analogy, in the C programming language, the `main` function is As an analogy, in the C programming language, the `main` function is
the unique access function and any function called from it would be an the unique main function and any function called from it would be an
entrypoint. entrypoint.
The parameter of the contract is then a variant type, and, depending The parameter of the contract is then a variant type, and, depending
on the constructors of that type, different functions in the contract on the constructors of that type, different functions in the contract
are called. In other terms, the unique access function dispatches the are called. In other terms, the unique main function dispatches the
control flow depending on a *pattern matching* on the contract control flow depending on a *pattern matching* on the contract
parameter. parameter.
@ -128,7 +128,7 @@ function entry_A (const n : nat; const store : storage) : return is
function entry_B (const s : string; const store : storage) : return is function entry_B (const s : string; const store : storage) : return is
((nil : list (operation)), store with record [name = s]) ((nil : list (operation)), store with record [name = s])
function access (const action : parameter; const store : storage): return is function main (const action : parameter; const store : storage): return is
case action of case action of
Action_A (n) -> entry_A (n, store) Action_A (n) -> entry_A (n, store)
| Action_B (s) -> entry_B (s, store) | Action_B (s) -> entry_B (s, store)
@ -154,7 +154,7 @@ let entry_A (n, store : nat * storage) : return =
let entry_B (s, store : string * storage) : return = let entry_B (s, store : string * storage) : return =
([] : operation list), {store with name = s} ([] : operation list), {store with name = s}
let access (action, store: parameter * storage) : return = let main (action, store: parameter * storage) : return =
match action with match action with
Action_A n -> entry_A (n, store) Action_A n -> entry_A (n, store)
| Action_B s -> entry_B (s, store) | Action_B s -> entry_B (s, store)
@ -179,7 +179,7 @@ let entry_A = ((n, store): (nat, storage)) : return =>
let entry_B = ((s, store): (string, storage)) : return => let entry_B = ((s, store): (string, storage)) : return =>
(([] : list (operation)), {...store, name : s}); (([] : list (operation)), {...store, name : s});
let access = ((action, store): (parameter, storage)) : return => let main = ((action, store): (parameter, storage)) : return =>
switch (action) { switch (action) {
| Action_A (n) => entry_A ((n, store)) | Action_A (n) => entry_A ((n, store))
| Action_B (s) => entry_B ((s, store)) | Action_B (s) => entry_B ((s, store))
@ -249,7 +249,7 @@ This example shows how `sender` or `source` can be used to deny access to an ent
```pascaligo group=c ```pascaligo group=c
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
function filter (const action : parameter; const store : storage) : return is function main (const action : parameter; const store : storage) : return is
if source =/= owner then (failwith ("Access denied.") : return) if source =/= owner then (failwith ("Access denied.") : return)
else ((nil : list(operation)), store) else ((nil : list(operation)), store)
``` ```
@ -258,7 +258,7 @@ function filter (const action : parameter; const store : storage) : return is
```cameligo group=c ```cameligo group=c
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
let filter (action, store: parameter * storage) : return = let main (action, store: parameter * storage) : return =
if source <> owner then (failwith "Access denied." : return) if source <> owner then (failwith "Access denied." : return)
else (([] : operation list), store) else (([] : operation list), store)
``` ```
@ -267,7 +267,7 @@ let filter (action, store: parameter * storage) : return =
```reasonligo group=c ```reasonligo group=c
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
let access = ((action, store): (parameter, storage)) : storage => { let main = ((action, store): (parameter, storage)) : storage => {
if (source != owner) { (failwith ("Access denied.") : return); } if (source != owner) { (failwith ("Access denied.") : return); }
else { (([] : list (operation)), store); }; else { (([] : list (operation)), store); };
}; };

View File

@ -11,13 +11,14 @@ We will be implementing a counter contract.
## Dry-running a Contract ## Dry-running a Contract
Testing a contract can be quite easy if we utilize LIGO's built-in dry Testing a contract can be quite easy if we utilize LIGO's built-in dry
run feature. Dry-run works by simulating the access function run feature. Dry-run works by simulating the main function execution,
execution, as if it were deployed on a real chain. You need to provide as if it were deployed on a real chain. You need to provide the
the following: following:
- `file` - contract to run - `file` - contract to run
- `entrypoint` - name of the function to execute - `entrypoint` - name of the function to execute
- `parameter` - parameter passed to the access function (in a theoretical invocation operation) - `parameter` - parameter passed to the main function (in a
theoretical invocation operation)
- `storage` - a mock storage value, as if it were stored on a real chain - `storage` - a mock storage value, as if it were stored on a real chain
Here is a full example: Here is a full example:
@ -33,15 +34,15 @@ ligo dry-run src/basic.ligo main Unit Unit
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
Output of the `dry-run` is the return value of our access function, we Output of the `dry-run` is the return value of our main function, we
can see the operations emited - in our case an empty list, and the new can see the operations emitted (in our case an empty list, and the new
storage value being returned - which in our case is still `Unit`. storage value being returned) which in our case is still `Unit`.
## A Counter Contract ## A Counter Contract
Our counter contract will store a single `int` as it's storage, and Our counter contract will store a single `int` as it's storage, and
will accept an `action` variant in order to re-route our single `main` will accept an `action` variant in order to re-route our single `main`
access function to two entrypoints for `addition` and `subtraction`. function to two entrypoints for `addition` and `subtraction`.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->

View File

@ -99,7 +99,7 @@ called the *head*, and the sub-list after the head is called the
think of a list a *stack*, where the top is written on the left. think of a list a *stack*, where the top is written on the left.
> 💡 Lists are needed when returning operations from a smart > 💡 Lists are needed when returning operations from a smart
> contract's access function. > contract's main function.
### Defining Lists ### Defining Lists

View File

@ -11,7 +11,7 @@ type return is list (operation) * taco_shop_storage
const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"
const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
block { block {
// Retrieve the taco_kind from the contract's storage or fail // Retrieve the taco_kind from the contract's storage or fail
const taco_kind : taco_supply = const taco_kind : taco_supply =

View File

@ -8,7 +8,7 @@ type taco_shop_storage is map (nat, taco_supply)
type return is list (operation) * taco_shop_storage type return is list (operation) * taco_shop_storage
function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is function main (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
block { block {
// Retrieve the taco_kind from the contract's storage or fail // Retrieve the taco_kind from the contract's storage or fail
const taco_kind : taco_supply = const taco_kind : taco_supply =

View File

@ -76,18 +76,18 @@ screenshot below:
<img src="/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png" /> <img src="/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png" />
<div style="opacity: 0.7; text-align: center; font-size: 12px; margin-top:-24px;">Installing the <b>next</b> version of LIGO's CLI</div> <div style="opacity: 0.7; text-align: center; font-size: 12px; margin-top:-24px;">Installing the <b>next</b> version of LIGO's CLI</div>
## Implementing our first access function ## Implementing our First `main` Function
> From now on we will get a bit more technical. If you run into > From now on we will get a bit more technical. If you run into
> something we have not covered yet - please try checking out the > something we have not covered yet - please try checking out the
> [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks. > [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks.
To begin implementing our smart contract, we need an *access To begin implementing our smart contract, we need a *main function*,
function*, that is the first function being executed. We will call it that is the first function being executed. We will call it `main` and
`main` and it will specify our contract's storage (`int`) and input it will specify our contract's storage (`int`) and input parameter
parameter (`int`). Of course this is not the final storage/parameter (`int`). Of course this is not the final storage/parameter of our
of our contract, but it is something to get us started and test our contract, but it is something to get us started and test our LIGO
LIGO installation as well. installation as well.
### `taco-shop.ligo` ### `taco-shop.ligo`
```pascaligo group=a ```pascaligo group=a
@ -99,7 +99,7 @@ list (operation) * int is
Let us break down the contract above to make sure we understand each Let us break down the contract above to make sure we understand each
bit of the LIGO syntax: bit of the LIGO syntax:
- **`function main`** - definition of the access function, which takes - **`function main`** - definition of the main function, which takes
a the parameter of the contract and the storage a the parameter of the contract and the storage
- **`(const parameter : int; const contractStorage : int)`** - - **`(const parameter : int; const contractStorage : int)`** -
parameters passed to the function: the first is called `parameter` parameters passed to the function: the first is called `parameter`
@ -129,11 +129,11 @@ also an `int`.
The `dry-run` command requires a few parameters: The `dry-run` command requires a few parameters:
- **contract** *(file path)* - **contract** *(file path)*
- **entrypoint** *(name of the access function in the contract)* - **entrypoint** *(name of the main function in the contract)*
- **parameter** *(parameter to execute our contract with)* - **parameter** *(parameter to execute our contract with)*
- **storage** *(starting storage before our contract's code is executed)* - **storage** *(starting storage before our contract's code is executed)*
It outputs what is returned from our access function: in our case a It outputs what is returned from our main function: in our case a
tuple containing an empty list (of operations to apply) and the new tuple containing an empty list (of operations to apply) and the new
storage value, which, in our case, is the sum of the previous storage storage value, which, in our case, is the sum of the previous storage
and the parameter we have used for the invocation. and the parameter we have used for the invocation.
@ -176,7 +176,7 @@ type taco_supply is record [
type taco_shop_storage is map (nat, taco_supply) type taco_shop_storage is map (nat, taco_supply)
``` ```
Next step is to update the `main` access function to include Next step is to update the `main` function to include
`taco_shop_storage` in its storage. In the meanwhile, let us set the `taco_shop_storage` in its storage. In the meanwhile, let us set the
`parameter` to `unit` as well to clear things up. `parameter` to `unit` as well to clear things up.
@ -249,17 +249,15 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map [
## Providing another Access Function for Buying Tacos ## Providing another Access Function for Buying Tacos
Now that we have our stock well defined in form of storage, we can Now that we have our stock well defined in form of storage, we can
move on to the actual sales. We will replace the `main` access move on to the actual sales. The `main` function will take a key `id`
function with another, `buy_taco`, that takes a key `id` from our from our `taco_shop_storage` map. This will allow us to calculate
`taco_shop_storage` map. This will allow us to calculate pricing, and pricing, and if the sale is successful, we will be able to reduce our
if the sale is successful, we will be able to reduce our stock because stock because we have sold a taco!
we have sold a taco!
### Selling the Tacos for Free ### Selling the Tacos for Free
Let is start by customizing our contract a bit, we will: Let is start by customizing our contract a bit, we will:
- rename the access function from `main` to `buy_taco`
- rename `parameter` to `taco_kind_index` - rename `parameter` to `taco_kind_index`
- change `taco_shop_storage` to a `var` instead of a `const`, because - change `taco_shop_storage` to a `var` instead of a `const`, because
we will want to modify it we will want to modify it
@ -275,7 +273,7 @@ type taco_shop_storage is map (nat, taco_supply)
type return is list (operation) * taco_shop_storage type return is list (operation) * taco_shop_storage
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
((nil : list (operation)), taco_shop_storage) ((nil : list (operation)), taco_shop_storage)
``` ```
@ -303,7 +301,7 @@ type taco_shop_storage is map (nat, taco_supply)
type return is list (operation) * taco_shop_storage type return is list (operation) * taco_shop_storage
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
block { block {
// Retrieve the taco_kind from the contract's storage or fail // Retrieve the taco_kind from the contract's storage or fail
const taco_kind : taco_supply = const taco_kind : taco_supply =
@ -352,7 +350,7 @@ type taco_shop_storage is map (nat, taco_supply)
type return is list (operation) * taco_shop_storage type return is list (operation) * taco_shop_storage
function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is function main (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
block { block {
// Retrieve the taco_kind from the contract's storage or fail // Retrieve the taco_kind from the contract's storage or fail
const taco_kind : taco_supply = const taco_kind : taco_supply =
@ -381,7 +379,7 @@ In order to test the `amount` sent, we will use the `--amount` option
of `dry-run`: of `dry-run`:
```zsh ```zsh
ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [ ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 main 1n "map [
1n -> record [ 1n -> record [
current_stock = 50n; current_stock = 50n;
max_price = 50tez max_price = 50tez