Skip to content

Commit

Permalink
Fix exercise in Chapter Polymorphism
Browse files Browse the repository at this point in the history
  • Loading branch information
OCTO-FRAH committed Jun 25, 2020
1 parent a2c0ed5 commit a2a5a24
Show file tree
Hide file tree
Showing 25 changed files with 311 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ let registerShip ((key,shipAddress, shipName, shipHostile, cs): (shipKey * addre

let sendTx ((e,callbackAddress) : (ship * address)): (operation list) =
// Type your solution below
let contractInterfaceOpt: actionSquadron contract option = Tezos.get_entrypoint_opt "%moduleResponse" callbackAddress in
let contractInterface : actionSquadron contract = match (contractInterfaceOpt) with
| Some (ci) -> ci
| None -> (failwith("Entrypoint not found in contract Squadron"): actionSquadron contract)
in
let ee : actionModuleResponse = { e = e } in
let sendbackOperation: operation = Tezos.transaction (ModuleResponse(ee)) 0mutez contractInterface in

let listoperation : operation list = [ sendbackOperation ] in
listoperation

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "central_types.mligo"
#include "squadron_types.mligo"

let registerShip ((key,shipAddress, shipName, shipHostile, cs): (shipKey * address * string * bool * centralStorage)): (operation list * centralStorage) =
let checkship: ship option = Map.find_opt key cs in
let modified_storage = match (checkship) with
| Some(e) -> (failwith("ship already registered") : centralStorage)
| None -> Map.add key {
id = shipAddress;
name = shipName;
hostile = shipHostile
} cs
in
(([]: operation list), modified_storage)

let sendTx ((e,callbackAddress) : (ship * address)): (operation list) =
// Type your solution below
let contractInterfaceOpt: actionSquadron contract option = Tezos.get_entrypoint_opt "%moduleResponse" callbackAddress in
let contractInterface : actionSquadron contract = match (contractInterfaceOpt) with
| Some (ci) -> ci
| None -> (failwith("Entrypoint not found in contract Squadron"): actionSquadron contract)
in
let ee : actionModuleResponse = { e = e } in
let sendbackOperation: operation = Tezos.transaction (ModuleResponse(ee)) 0mutez contractInterface in
let listoperation : operation list = [ sendbackOperation ] in
listoperation

let retrieveShip ((key, callbackAddress, cs): (shipKey * address * centralStorage)) : (operation list * centralStorage) =
let checkship: ship option = Map.find_opt key cs in
let listop: operation list = match (checkship) with
| Some(e) -> sendTx((e, callbackAddress))
| None -> (failwith("no ship") : operation list)
in
(listop , cs)

let central ((action,cs) : (actionCentral * centralStorage)) : (operation list * centralStorage) =
match (action) with
| RegisterShip (ar) -> registerShip ((ar.sKey, ar.sAddr, ar.sName, ar.sHostile, cs))
| RetrieveShip (ret) -> retrieveShip ((ret.sKey, ret.callbackAddress, cs))
57 changes: 43 additions & 14 deletions src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/course.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,69 @@

<dialog character="pilot"></dialog>

When sending transactions between contracts, each contract must know the target contract interface and the parameter type of the target contract. This is done basicaly by separating type definition and function implementation and by using inclusion. But a problem arises when creating a new contract which must communicate way and back (two-way communication) with an already deployed contract. The deployed contract cannot know the signature of a contract not yet created!
When sending transactions between contracts, each contract must know the target contract interface and the parameter type of the target contract. This is done basically by separating type definition and function implementation and by using inclusion.

But a problem arises when creating a new contract which must communicate way and back (two-way communication) with an already deployed contract. The deployed contract cannot know the signature of a contract not yet created !

<!-- prettier-ignore -->Hopefully a solution exists to this problem of polymorphism, it is *Tezos.get\_entrypoint\_opt* function.

## Way and back communication between contracts

Let's consider two contracts _A_ and _B_. Contract _A_ ask some information from contract _B_ and they communicate between each other with transactions.
A sends a request to B, (it means _A_ calls an entrypoint of _B_, so contract _A_ includes type definition of _B_)
B receives the request, process the request and sends an response back to _A_ (it means _B_ calls an entrypoint of _A_, , so contract _B_ includes type definition of _A_)
A sends a request to B, (it means _A_ calls an entry point of _B_, so contract _A_ includes type definition of _B_)
B receives the request, process the request and sends an response back to _A_ (it means _B_ calls an entry point of _A_, , so contract _B_ includes type definition of _A_)
Once they are deployed, we cannot change their includes.

Now let's consider a third smart contract _C_ which will communicate with _B_. (Like _A_)
Since we can't change _B_ (already deployed) , then _C_ must have same definition of _A_ to be able to receive transactions from _B_.

The problem is coming from the fact that _B_ must know the whole definition of _A_ parameter but it actually only needs one entrypoint used for the transaction. If _C_ implements the same entrypoint than _A_ (for receiving a message from _B_) then transaction will match the entrypoint and problems solved !
The problem is coming from the fact that _B_ must know the whole definition of _A_ parameter but it actually only needs one entry point used for the transaction. If _C_ implements the same entry point than _A_ (for receiving a message from _B_) then transaction will match the entry point and problems solved !

## Retrieving entry points

<!-- prettier-ignore -->For this purpose, the predefined function *Tezos.get\_entrypoint\_opt* can be used to retrieve the definition of a single entrypoint (from the whole variant).
<!-- prettier-ignore -->For this purpose, the predefined function *Tezos.get\_entrypoint\_opt* can be used to retrieve the definition of a single entry point (from the whole variant).

<!-- prettier-ignore -->The predefined function *Tezos.get\_entrypoint\_opt* can be used in replacement of the *Tezos.get\_contract\_opt* function to retrieve contract interface but for only one entrypoint. It takes the requested entrypoint as parameter (with a special michelson syntax) and the address of the contract.
<!-- prettier-ignore -->The predefined function *Tezos.get\_entrypoint\_opt* can be used in replacement of the *Tezos.get\_contract\_opt* function to retrieve contract interface but for only one entry point. It takes the requested entry point as parameter (with a special michelson syntax) and the address of the contract.

The predefined function *Tezos.get\_entrypoint\_opt* has the following syntax :

```
const <variable_name>: contract(<type_of_target_contract_parameter>) = Tezos.get_entrypoint_opt(<entrypoint_name>, <target_contract_address>);
let <variable_name>: option(contract(<type_of_target_contract_parameter>)) = Tezos.get_entrypoint_opt(<entrypoint_name>, <target_contract_address>);
```

<!-- prettier-ignore --> *entrypoint\_name* is a double-quoted string with first character is % followed by the name of the entrypoint (and its first letter must not be capitalized) exemple: for an entrypoint FooBar the corresponding *entrypoint\_name* is "%fooBar"
<!-- prettier-ignore -->When function *get\_entrypoint\_opt* does not find any contract at a given _address_ or the contract doesn't match the type, then _None_ is returned.

As for *Tezos.get\_contract\_opt* function, *Tezos.get\_entrypoint\_opt* function returns an _option_ type of entry point.


## Entry point naming convention

<!-- prettier-ignore --> *entrypoint\_name* is a double-quoted string with first character is % followed by the name of the entry point (and its first letter must not be capitalized) exemple: for an entry point *FooBar* the corresponding *entrypoint\_name* is *"%fooBar"*

<!-- prettier-ignore --> To get a contract from an address and entry point, we can use _Tezos.get_entrypoint_opt(<entrypoint>, <address>)_

Entrypoints are written in the form of: _%myEntryPoint_ for the entry point _MyEntryPoint_. Notice we change the case of the first letter.


<!-- prettier-ignore --> To get a contract from an address and entrypoint, we can use _Tezos.get_entrypoint_opt(<entrypoint>, <address>)_

Entrypoints are written in the form of: _%myEntryPoint_ for the enty point _MyEntryPoint_. Notice we change the case of the first letter. When no contract is found or the contract doesn't match the type, _None_ is returned. Remember to use _option_ for the return type as it is optional.

## Your mission

<!-- prettier-ignore -->1- Consider the following smart contract :
Consider the following smart contracts : Squadron and Central.

```
Central contract acts as an inventory of ships (an entry point *RegisterShip* is provided to register a ship).
Central contract can provide information of a ship to a calling contract via a callback transaction (an entry point *RetrieveShip* is provided to query a ship).
Squadron contract provides an entry point *ModuleRequest* to ask ship information to the central contract.
Squadron contract provides an entry point *ModuleResponse* which is called by the central contract when sending back the expected ship information.

```
As you can see, the entry point *RetrieveShip* calls the function *sendTx* which is responsible to send a transaction to a the calling contract. The implementation of Central contract has not been finished. We need you to finish the *sendTx* function !


<!-- prettier-ignore -->1- Try to retrieve the entry point %moduleResponse of the given *callbackAddress* and store the result in a variable called *contractInterfaceOpt* of type _option(contract(actionSquadron))_

<!-- prettier-ignore -->2- Use a _switch_ operator to extract the entry point if it exists (use temporary variable name *ci* in the switch). Otherwise throw an exception with error message "Entrypoint not found in contract Squadron". The extracted entry point must be stored in a variable called *contractInterface*.

<!-- prettier-ignore -->3- In order to prepare the ship information that need to be sent back to the Squadron contract. Check the expected type of entry point _moduleResponse_ and prepare a variable *ee* containing the expected ship *e*.

<!-- prettier-ignore -->4- Send a transaction to the retrieved entry point of squadron contract. the transaction must point to the moduleResponse entrypoint of squadron contract and passing the right argument prepared in step 3. This transaction sends no money. The transaction is an _operation_ type that you can store in a variable *sendbackOperation*.

<!-- prettier-ignore -->2-

This file was deleted.

15 changes: 12 additions & 3 deletions src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
import course from "!raw-loader!./course.md";
/* eslint import/no-webpack-loader-syntax: off */
// @ts-ignore
import exercise from "!raw-loader!./exercise.ligo";
import support1 from "!raw-loader!./squadron.mligo";
/* eslint import/no-webpack-loader-syntax: off */
// @ts-ignore
import solution from "!raw-loader!./solution.ligo";
import support2 from "!raw-loader!./squadron_types.mligo";
/* eslint import/no-webpack-loader-syntax: off */
// @ts-ignore
import support3 from "!raw-loader!./central_types.mligo";
/* eslint import/no-webpack-loader-syntax: off */
// @ts-ignore
import exercise from "!raw-loader!./central.mligo";
/* eslint import/no-webpack-loader-syntax: off */
// @ts-ignore
import solution from "!raw-loader!./central_solution.mligo";

export const data = { course, exercise, solution };
export const data = { course, exercise, solution, support1, support2, support3 };

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ function registerShip(const key: shipKey; const shipAddress: address; const ship
function sendTx(const e: ship; const callbackAddress: address): (list(operation)) is
begin
// Type your solution below
const contractInterface: contract(actionSquadron) = get_entrypoint("%moduleResponse", callbackAddress);
const ee : actionModuleResponse = record
e = e
end;
const sendbackOperation: operation = transaction(ModuleResponse(ee), 0mutez, contractInterface);

const listoperation : list(operation) = list
sendbackOperation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "central_types.ligo"
#include "squadron_types.ligo"

function registerShip(const key: shipKey; const shipAddress: address; const shipName: string; const shipHostile: bool; const cs: centralStorage): (list(operation) * centralStorage) is
begin
const checkship: option(ship) = cs[key];
case checkship of
| Some(e) -> failwith("ship already registered")
| None -> begin
cs[key] := record
id = shipAddress;
name = shipName;
hostile = shipHostile;
end
end
end
end with ((nil: list(operation)) , cs)

function sendTx(const e: ship; const callbackAddress: address): (list(operation)) is
begin
// Type your solution below
const contractInterfaceOpt: option(contract(actionSquadron)) = Tezos.get_entrypoint_opt("%moduleResponse", callbackAddress);
const contractInterface: contract(actionSquadron) = case contractInterfaceOpt of
| Some(ci) -> ci
| None -> (failwith("Entrypoint not found in contract Squadron"): contract(actionSquadron))
end;
const ee : actionModuleResponse = record
e = e
end;
const sendbackOperation: operation = transaction(ModuleResponse(ee), 0mutez, contractInterface);

const listoperation : list(operation) = list
sendbackOperation
end
end with (listoperation)

function retrieveShip(const key: shipKey; const callbackAddress: address; const cs: centralStorage): (list(operation) * centralStorage) is
begin
const checkship: option(ship) = cs[key];
var listop: list(operation) := list end;
case checkship of
| Some(e) -> begin
listop := sendTx(e, callbackAddress);
end
| None -> failwith("no ship")
end
end with (listop , cs)

function central(const action : actionCentral; const cs: centralStorage) : (list(operation) * centralStorage) is
block {skip} with
case action of
| RegisterShip (ar) -> registerShip (ar.sKey, ar.sAddr, ar.sName, ar.sHostile, cs)
| RetrieveShip (ret) -> retrieveShip(ret.sKey, ret.callbackAddress, cs)
end
Loading

0 comments on commit a2a5a24

Please sign in to comment.