From a2a5a24ceb49423fefc84a349b85afd874cc378b Mon Sep 17 00:00:00 2001 From: Frank Hillard Date: Thu, 25 Jun 2020 12:33:35 +0200 Subject: [PATCH] Fix exercise in Chapter Polymorphism --- .../Camel/ChapterPolymorphism/central.mligo | 8 +-- .../central_solution.mligo | 39 +++++++++++++ .../Camel/ChapterPolymorphism/course.md | 57 ++++++++++++++----- .../Camel/ChapterPolymorphism/exercise.ligo | 10 ---- .../Camel/ChapterPolymorphism/index.ts | 15 ++++- .../Camel/ChapterPolymorphism/solution.ligo | 10 ---- .../Pascal/ChapterPolymorphism/central.ligo | 5 -- .../ChapterPolymorphism/central_solution.ligo | 54 ++++++++++++++++++ .../Pascal/ChapterPolymorphism/course.md | 57 ++++++++++++++----- .../Pascal/ChapterPolymorphism/exercise.ligo | 10 ---- .../Pascal/ChapterPolymorphism/index.ts | 15 ++++- .../Pascal/ChapterPolymorphism/solution.ligo | 10 ---- .../Reason/ChapterInteractions/course.md | 4 +- .../{exercise.ligo => exercise.religo} | 0 .../Reason/ChapterInteractions/index.ts | 4 +- .../{solution.ligo => solution.religo} | 0 .../Reason/ChapterPolymorphism/central.ligo | 8 +-- .../central_solution.religo | 48 ++++++++++++++++ ...entral_types.ligo => central_types.religo} | 0 .../Reason/ChapterPolymorphism/course.md | 55 +++++++++++++----- .../Reason/ChapterPolymorphism/exercise.ligo | 10 ---- .../Reason/ChapterPolymorphism/index.ts | 15 ++++- .../Reason/ChapterPolymorphism/solution.ligo | 10 ---- .../{squadron.ligo => squadron.religo} | 0 ...adron_types.ligo => squadron_types.religo} | 0 25 files changed, 311 insertions(+), 133 deletions(-) create mode 100644 src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central_solution.mligo delete mode 100644 src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/exercise.ligo delete mode 100644 src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/solution.ligo create mode 100644 src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central_solution.ligo delete mode 100644 src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/exercise.ligo delete mode 100644 src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/solution.ligo rename src/frontend/src/pages/Chapters/Reason/ChapterInteractions/{exercise.ligo => exercise.religo} (100%) rename src/frontend/src/pages/Chapters/Reason/ChapterInteractions/{solution.ligo => solution.religo} (100%) create mode 100644 src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_solution.religo rename src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/{central_types.ligo => central_types.religo} (100%) delete mode 100644 src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/exercise.ligo delete mode 100644 src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/solution.ligo rename src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/{squadron.ligo => squadron.religo} (100%) rename src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/{squadron_types.ligo => squadron_types.religo} (100%) diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central.mligo b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central.mligo index b3b5bc4..df6c3d3 100644 --- a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central.mligo +++ b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central.mligo @@ -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 diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central_solution.mligo b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central_solution.mligo new file mode 100644 index 0000000..b3b5bc4 --- /dev/null +++ b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/central_solution.mligo @@ -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)) diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/course.md b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/course.md index 6de752e..1cce59a 100644 --- a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/course.md +++ b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/course.md @@ -4,40 +4,69 @@ -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 ! 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 -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). +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). -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. +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 : contract() = Tezos.get_entrypoint_opt(, ); +let : option(contract()) = Tezos.get_entrypoint_opt(, ); ``` - *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" +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 + + *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"* + + To get a contract from an address and entry point, we can use _Tezos.get_entrypoint_opt(,
)_ + +Entrypoints are written in the form of: _%myEntryPoint_ for the entry point _MyEntryPoint_. Notice we change the case of the first letter. + - To get a contract from an address and entrypoint, we can use _Tezos.get_entrypoint_opt(,
)_ -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 -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 ! + + +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))_ + +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*. + +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*. + +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*. -2- diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/exercise.ligo b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/exercise.ligo deleted file mode 100644 index b92e9df..0000000 --- a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/exercise.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/index.ts b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/index.ts index 5ba0a9f..968970d 100644 --- a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/index.ts +++ b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/index.ts @@ -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 }; diff --git a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/solution.ligo b/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/solution.ligo deleted file mode 100644 index e55420a..0000000 --- a/src/frontend/src/pages/Chapters/Camel/ChapterPolymorphism/solution.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - Tezos.transaction (unit, purchase_price, vendor_address) -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central.ligo b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central.ligo index 5eabd3a..057e102 100644 --- a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central.ligo +++ b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central.ligo @@ -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 diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central_solution.ligo b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central_solution.ligo new file mode 100644 index 0000000..576536b --- /dev/null +++ b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/central_solution.ligo @@ -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 \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/course.md b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/course.md index a65f753..1cce59a 100644 --- a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/course.md +++ b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/course.md @@ -4,40 +4,69 @@ -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 ! 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 -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). +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). -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. +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 : option(contract()) = Tezos.get_entrypoint_opt(, ); +let : option(contract()) = Tezos.get_entrypoint_opt(, ); ``` - *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" +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 + + *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"* + + To get a contract from an address and entry point, we can use _Tezos.get_entrypoint_opt(,
)_ + +Entrypoints are written in the form of: _%myEntryPoint_ for the entry point _MyEntryPoint_. Notice we change the case of the first letter. + - To get a contract from an address and entrypoint, we can use _Tezos.get_entrypoint_opt(,
)_ -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 -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 ! + + +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))_ + +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*. + +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*. + +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*. -2- Complete diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/exercise.ligo b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/exercise.ligo deleted file mode 100644 index b92e9df..0000000 --- a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/exercise.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/index.ts b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/index.ts index 5ba0a9f..1fe11f9 100644 --- a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/index.ts +++ b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/index.ts @@ -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.ligo"; /* eslint import/no-webpack-loader-syntax: off */ // @ts-ignore -import solution from "!raw-loader!./solution.ligo"; +import support2 from "!raw-loader!./squadron_types.ligo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import support3 from "!raw-loader!./central_types.ligo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import exercise from "!raw-loader!./central.ligo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import solution from "!raw-loader!./central_solution.ligo"; -export const data = { course, exercise, solution }; +export const data = { course, exercise, solution, support1, support2, support3 }; \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/solution.ligo b/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/solution.ligo deleted file mode 100644 index e55420a..0000000 --- a/src/frontend/src/pages/Chapters/Pascal/ChapterPolymorphism/solution.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - Tezos.transaction (unit, purchase_price, vendor_address) -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterInteractions/course.md b/src/frontend/src/pages/Chapters/Reason/ChapterInteractions/course.md index c98757a..a4061d8 100644 --- a/src/frontend/src/pages/Chapters/Reason/ChapterInteractions/course.md +++ b/src/frontend/src/pages/Chapters/Reason/ChapterInteractions/course.md @@ -16,7 +16,7 @@ let : operation = Tezos.transaction (, , { // Type your solution below - let contractInterfaceOpt: option(contract(actionSquadron)) = Tezos.get_entrypoint_opt("%moduleResponse", callbackAddress); - let contractInterface : contract(actionSquadron) = switch (contractInterfaceOpt) { - | Some (ci) => ci - | None => (failwith("Entrypoint not found in contract Squadron"): contract(actionSquadron)) - }; - let ee : actionModuleResponse = { e : e }; - let sendbackOperation: operation = Tezos.transaction (ModuleResponse(ee), 0mutez, contractInterface); + let listoperation : list(operation) = [ sendbackOperation ]; listoperation; } diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_solution.religo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_solution.religo new file mode 100644 index 0000000..d7df407 --- /dev/null +++ b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_solution.religo @@ -0,0 +1,48 @@ +#include "central_types.religo" +#include "squadron_types.religo" + +let registerShip = ((key,shipAddress, shipName, shipHostile, cs): (shipKey, address, string, bool, centralStorage)): (list(operation), centralStorage) => +{ + let checkship: option(ship) = Map.find_opt (key,cs); + let modified_storage = switch (checkship) { + | Some(e) => (failwith("ship already registered") : centralStorage) + | None => Map.add (key, { + id : shipAddress, + name : shipName, + hostile : shipHostile + }, cs) + }; + (([]: list(operation)) , modified_storage); +} + +let sendTx = ((e,callbackAddress) : (ship, address)): (list(operation)) => +{ + // Type your solution below + let contractInterfaceOpt: option(contract(actionSquadron)) = Tezos.get_entrypoint_opt("%moduleResponse", callbackAddress); + let contractInterface : contract(actionSquadron) = switch (contractInterfaceOpt) { + | Some (ci) => ci + | None => (failwith("Entrypoint not found in contract Squadron"): contract(actionSquadron)) + }; + let ee : actionModuleResponse = { e : e }; + let sendbackOperation: operation = Tezos.transaction (ModuleResponse(ee), 0mutez, contractInterface); + let listoperation : list(operation) = [ sendbackOperation ]; + listoperation; +} + +let retrieveShip = ((key, callbackAddress, cs): (shipKey, address, centralStorage)) : (list(operation), centralStorage) => +{ + let checkship: option(ship) = Map.find_opt (key, cs); + let listop: list(operation) = switch (checkship) { + | Some(e) => sendTx((e, callbackAddress)) + | None => (failwith("no ship") : list(operation)) + }; + (listop , cs); +} + +let central = ((action,cs) : (actionCentral, centralStorage)) : (list(operation), centralStorage) => +{ + switch (action) { + | RegisterShip (ar) => registerShip ((ar.sKey, ar.sAddr, ar.sName, ar.sHostile, cs)) + | RetrieveShip (ret) => retrieveShip ((ret.sKey, ret.callbackAddress, cs)) + } +} \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_types.ligo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_types.religo similarity index 100% rename from src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_types.ligo rename to src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/central_types.religo diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/course.md b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/course.md index fe3f5da..1cce59a 100644 --- a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/course.md +++ b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/course.md @@ -4,40 +4,69 @@ -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 ! 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 -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). +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). -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. +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 : ``` let : option(contract()) = Tezos.get_entrypoint_opt(, ); ``` - *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" +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 + + *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"* + + To get a contract from an address and entry point, we can use _Tezos.get_entrypoint_opt(,
)_ + +Entrypoints are written in the form of: _%myEntryPoint_ for the entry point _MyEntryPoint_. Notice we change the case of the first letter. + - To get a contract from an address and entrypoint, we can use _Tezos.get_entrypoint_opt(,
)_ -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 -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 ! + + +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))_ + +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*. + +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*. + +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*. -2- diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/exercise.ligo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/exercise.ligo deleted file mode 100644 index b92e9df..0000000 --- a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/exercise.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/index.ts b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/index.ts index 5ba0a9f..e9f2e77 100644 --- a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/index.ts +++ b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/index.ts @@ -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.religo"; /* eslint import/no-webpack-loader-syntax: off */ // @ts-ignore -import solution from "!raw-loader!./solution.ligo"; +import support2 from "!raw-loader!./squadron_types.religo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import support3 from "!raw-loader!./central_types.religo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import exercise from "!raw-loader!./central.religo"; +/* eslint import/no-webpack-loader-syntax: off */ +// @ts-ignore +import solution from "!raw-loader!./central_solution.religo"; -export const data = { course, exercise, solution }; +export const data = { course, exercise, solution, support1, support2, support3 }; \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/solution.ligo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/solution.ligo deleted file mode 100644 index e55420a..0000000 --- a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/solution.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function purchase (const purchase_price : tez) : bool is - const ship_address : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); - const vendor_address : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); - - if Tezos.source =/= ship_address then failwith ("Access denied"); - if Tezos.amount =/= purchase_price then failwith ("Incorrect amount"); - - // Type your solution below - Tezos.transaction (unit, purchase_price, vendor_address) -return True \ No newline at end of file diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron.ligo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron.religo similarity index 100% rename from src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron.ligo rename to src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron.religo diff --git a/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron_types.ligo b/src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron_types.religo similarity index 100% rename from src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron_types.ligo rename to src/frontend/src/pages/Chapters/Reason/ChapterPolymorphism/squadron_types.religo