From 3a2b496e3d9a4bcd821fa055a04500133d3b7ce8 Mon Sep 17 00:00:00 2001 From: iamveritas Date: Thu, 11 Jun 2020 13:39:07 +0300 Subject: [PATCH 1/7] fixes #905 [eosio.cdt:issue:905] [docs] as of eosio 2.0-rc1 deferred transaction are deprecated --- docs/05_best-practices/09_deferred_transactions.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/05_best-practices/09_deferred_transactions.md b/docs/05_best-practices/09_deferred_transactions.md index 63ca63c2aa..c2d4a7eb11 100644 --- a/docs/05_best-practices/09_deferred_transactions.md +++ b/docs/05_best-practices/09_deferred_transactions.md @@ -6,5 +6,7 @@ Deferred communication conceptually takes the form of action notifications sent As already mentioned, deferred communication will get scheduled later at the producer's discretion. From the perspective of the originating transaction, i.e., the transaction that creates the deferred transaction, it can only determine whether the create request was submitted successfully or whether it failed (if it fails, it will fail immediately). Deferred transactions carry the authority of the contract that sends them. A transaction can cancel a deferred transaction. -[[warning | Warning about deferred transaction usage]] -| Because of the above, it is not recommended to use `deferred transactions`. There is consideration to deprecate deferred transactions in a future version. +Because all of the above it is not recommended to use `deferred transactions`. + +[[warning | Deferred Transactions Are Deprecated]] +| As of [EOSIO 2.0 RC1](https://github.com/EOSIO/eos/releases/tag/v2.0.0-rc1) deferred transactions are deprecated. From 0b5c7152a06f4bd14a4d1ddf641cffe160132a0e Mon Sep 17 00:00:00 2001 From: iamveritas Date: Thu, 11 Jun 2020 20:37:13 +0300 Subject: [PATCH 2/7] fixes #865 [eosio.cdt:issue:865] Introduce the eosio::check(eosio::has_auth(...)) in the existing how to --- ...to_restrict_access_to_an_action_by_user.md | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md index 4ec1229724..4002a87446 100644 --- a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md +++ b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md @@ -1,13 +1,41 @@ --- -content_title: How to restrict access to an action by a user +content_title: How To Perform Authorization Checks +link_text: How To Perform Authorization Checks --- ## Preconditions -- It is assumed you have the sources for a contract and one of the actions defined is getting as a parameter an account name and it is printing the account name. -To restrict access to the `hi` action, you can do it in two ways: +It is assumed the following: + +1. You have the sources of a contract with one of the actions defined, let's call it `hi` action. +2. The `hi` action has defined one input parameter `user` of type `name`. +3. The `hi` action is printing the name of the `user` account. +4. The `hi` action needs to authorize the `user` account. + +## Authorization Methods + +To restrict access to the `hi` action, you can do it in three ways. + +### 1. Using eosio::check(eosio::has_auth(...)...) + +The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account is using to sign the transaction (e.g. owner, active, code). + +[[info | Error message is custom]] +| Observe that in this case the yielded error message is a custom one and thus it can be used to provide a better experience for the user. + +```cpp +#include + +void hi( name user ) { + check(has_auth(user), "User is not authorized to perform this action."); + print( "Hello, ", name{user} ); +} +``` + +Another example can be found in the [Tic Tac Toe Tutorial](https://developers.eos.io/welcome/latest/tutorials/tic-tac-toe-game-contract/#action-handler---move). + +### 2. Using require_auth -1. Using require_auth The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account is using to sign the transaction (e.g. owner, active, code). ```cpp @@ -17,7 +45,10 @@ void hi( name user ) { } ``` -2. Or using require_auth2 +[[info | Error message is not custom]] +| Note that this time you can not customize the yielded error message, it will be a generic authorization error message. + +### 3. Using require_auth2 The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action and only if the permission used to sign the transaction is the 'active' one. In other words, if the same user is signing the transaction with a different permission (e.g. code, owner) the execution of the action is halted. @@ -25,9 +56,10 @@ The below code is enforcing the action `hi` to be executed only by the account t #include void hi( name user ) { - require_auth2(nm.value, "active"_n.value); + require_auth2(user.value, "active"_n.value); print( "Hello, ", name{user} ); } ``` -An example of this contract can be found [here](https://github.com/EOSIO/eosio.cdt/blob/master/examples/hello/src/hello.cpp) +[[info | Error message is not custom]] +| Note that this time, as well as previous method, you can not customize the yielded error message, it will be a generic authorization error message. From 64b74c9c57d151359c47e665f8ce9a0fd02e6630 Mon Sep 17 00:00:00 2001 From: iamveritas Date: Fri, 12 Jun 2020 13:32:40 +0300 Subject: [PATCH 3/7] fixes #866 [eosio.cdt:issue:866] Corrections are needed on the best practices, securing your contract --- .../05_securing_your_contract.md | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/05_best-practices/05_securing_your_contract.md b/docs/05_best-practices/05_securing_your_contract.md index 122e5c52d7..a69c136060 100644 --- a/docs/05_best-practices/05_securing_your_contract.md +++ b/docs/05_best-practices/05_securing_your_contract.md @@ -2,16 +2,35 @@ content_title: Securing your contract --- -These are basic recommendations that should be the foundation of securing your smart contract: +## Basic Recommendations -1. The master git branch has the `has_auth`, `require_auth`, `require_auth2` and `require_recipient` methods available in the EOSIO library. They can be found in detail [here](https://eosio.github.io/eosio.cdt/1.6.0-rc1/group__action.html#function-requirerecipient) and implemented [here](https://github.com/EOSIO/eos/blob/3fddb727b8f3615917707281dfd3dd3cc5d3d66d/libraries/chain/apply_context.cpp#L144) (they end up calling the methods implemented in the `apply_context` class). +The following are basic recommendations which can be the foundation of securing your smart contract. -2. Understand how each of your contracts' actions is impacting the RAM, CPU, and NET consumption, and which account ends up paying for these resources. +### 1. Authorization Checks -3. Have a solid and comprehensive development process that includes security considerations from day one of the product planning and development. +The following methods are available in the `EOSIO` library and they can be used to implemented authorization checks in your smart contracts: -4. Test your smart contracts with every update announced for the blockchain you have deployed to. To ease your work, automate the testing as much as possible so you can run them often, and improve them periodically. +- [`has_auth`](../group__action/#function-has_auth) +- [`require_auth`](../group__action/#function-require_auth) +- [`require_auth2`](../how-to-guides/authorization/how_to_restrict_access_to_an_action_by_user/#3-using-require_auth2) +- [`require_recipient`](../group__action/#function-require_recipient) -5. Conduct independent smart contract audits, at least two from different organizations. +### 2. Resource Management -6. Host periodic bug bounties on your smart contracts and keep a continuous commitment to reward real security problems reported at any time. \ No newline at end of file +Understand how each of your contracts' actions is impacting the RAM, CPU, and NET consumption, and which account ends up paying for these resources. + +### 3. Secure by Default + +Have a solid and comprehensive development process that includes security considerations from day one of the product planning and development. + +### 4. Continuous Integration And Continuous Delivery + +Test your smart contracts with every update announced for the blockchain you have deployed to. To ease your work, automate the testing as much as possible so you can run them often, and improve them periodically. + +### 5. Security Audits + +Conduct independent smart contract audits, at least two from different organizations. + +### 6. Bug Bounties + +Host periodic bug bounties on your smart contracts and keep a continuous commitment to reward real security problems reported at any time. From eb6aac120b2447ec3a00a7572971909127341f99 Mon Sep 17 00:00:00 2001 From: iamveritas Date: Fri, 12 Jun 2020 13:40:56 +0300 Subject: [PATCH 4/7] fixes #871 "for the Storage usage of the updated row" lowercase inconsistent --- libraries/eosiolib/contracts/eosio/multi_index.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/eosiolib/contracts/eosio/multi_index.hpp b/libraries/eosiolib/contracts/eosio/multi_index.hpp index f66c39f29b..bdd7c625b0 100644 --- a/libraries/eosiolib/contracts/eosio/multi_index.hpp +++ b/libraries/eosiolib/contracts/eosio/multi_index.hpp @@ -1571,7 +1571,7 @@ class multi_index * @ingroup multiindex * * @param itr - an iterator pointing to the object to be updated - * @param payer - account name of the payer for the Storage usage of the updated row + * @param payer - account name of the payer for the storage usage of the updated row * @param updater - lambda function that updates the target object * * @pre itr points to an existing element @@ -1618,7 +1618,7 @@ class multi_index * @ingroup multiindex * * @param obj - a reference to the object to be updated - * @param payer - account name of the payer for the Storage usage of the updated row + * @param payer - account name of the payer for the storage usage of the updated row * @param updater - lambda function that updates the target object * * @pre obj is an existing object in the table From 96b3eba8bd43fbdc33494ea3c15e48ffdd7cf8aa Mon Sep 17 00:00:00 2001 From: iamveritas Date: Fri, 12 Jun 2020 14:10:41 +0300 Subject: [PATCH 5/7] fixes #877 delete duplicated lines in eosio::datastream class documentation --- libraries/eosiolib/core/eosio/datastream.hpp | 45 +------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/libraries/eosiolib/core/eosio/datastream.hpp b/libraries/eosiolib/core/eosio/datastream.hpp index acbdbf3b47..924827148e 100644 --- a/libraries/eosiolib/core/eosio/datastream.hpp +++ b/libraries/eosiolib/core/eosio/datastream.hpp @@ -88,7 +88,6 @@ class datastream { /** * Writes a byte into the stream * - * @brief Writes a byte into the stream * @param c byte to write * @return true */ @@ -102,7 +101,6 @@ class datastream { /** * Reads a byte from the stream * - * @brief Reads a byte from the stream * @param c - The reference to destination byte * @return true */ @@ -111,7 +109,6 @@ class datastream { /** * Reads a byte from the stream * - * @brief Reads a byte from the stream * @param c - The reference to destination byte * @return true */ @@ -126,7 +123,6 @@ class datastream { /** * Retrieves the current position of the stream * - * @brief Retrieves the current position of the stream * @return T - The current position of the stream */ T pos()const { return _pos; } @@ -135,7 +131,6 @@ class datastream { /** * Sets the position within the current stream * - * @brief Sets the position within the current stream * @param p - The offset relative to the origin * @return true if p is within the range * @return false if p is not within the rawnge @@ -145,7 +140,6 @@ class datastream { /** * Gets the position within the current stream * - * @brief Gets the position within the current stream * @return p - The position within the current stream */ inline size_t tellp()const { return size_t(_pos - _start); } @@ -153,27 +147,20 @@ class datastream { /** * Returns the number of remaining bytes that can be read/skipped * - * @brief Returns the number of remaining bytes that can be read/skipped * @return size_t - The number of remaining bytes */ inline size_t remaining()const { return _end - _pos; } private: /** * The start position of the buffer - * - * @brief The start position of the buffer */ T _start; /** * The current position of the buffer - * - * @brief The current position of the buffer */ T _pos; /** * The end position of the buffer - * - * @brief The end position of the buffer */ T _end; }; @@ -224,7 +211,6 @@ class datastream { /** * Set new size * - * @brief Set new size * @param p - The new size * @return true */ @@ -454,7 +440,6 @@ inline datastream& operator<<(datastream& ds, const bool& d) { /** * Deserialize a bool from a stream * - * @brief Deserialize a bool * @param ds - The stream to read * @param d - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -524,7 +509,6 @@ datastream& operator << ( datastream& ds, const std::array& /** * Deserialize a fixed size std::array * - * @brief Deserialize a fixed size std::array * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -543,7 +527,6 @@ namespace _datastream_detail { /** * Check if type T is a pointer * - * @brief Check if type T is a pointer * @tparam T - The type to be checked * @return true if T is a pointer * @return false otherwise @@ -558,7 +541,6 @@ namespace _datastream_detail { /** * Check if type T is a primitive type * - * @brief Check if type T is a primitive type * @tparam T - The type to be checked * @return true if T is a primitive type * @return false otherwise @@ -572,7 +554,6 @@ namespace _datastream_detail { /* * Check if type T is a specialization of datastream * - * @brief Check if type T is a datastream * @tparam T - The type to be checked */ template @@ -582,9 +563,9 @@ namespace _datastream_detail { } /** - * Pointer should not be serialized, so this function will always throws an error + * Deserialize a pointer * - * @brief Deserialize a a pointer + * @brief Pointer should not be serialized, so this function will always throws an error * @param ds - The stream to read * @tparam Stream - Type of datastream buffer * @tparam T - Type of the pointer @@ -600,7 +581,6 @@ datastream& operator >> ( datastream& ds, T ) { /** * Serialize a fixed size C array of non-primitive and non-pointer type * - * @brief Serialize a fixed size C array of non-primitive and non-pointer type * @param ds - The stream to write * @param v - The value to serialize * @tparam Stream - Type of datastream buffer @@ -620,7 +600,6 @@ datastream& operator << ( datastream& ds, const T (&v)[N] ) { /** * Serialize a fixed size C array of primitive type * - * @brief Serialize a fixed size C array of primitive type * @param ds - The stream to write * @param v - The value to serialize * @tparam Stream - Type of datastream buffer @@ -638,7 +617,6 @@ datastream& operator << ( datastream& ds, const T (&v)[N] ) { /** * Deserialize a fixed size C array of non-primitive and non-pointer type * - * @brief Deserialize a fixed size C array of non-primitive and non-pointer type * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam T - Type of the object contained in the array @@ -661,7 +639,6 @@ datastream& operator >> ( datastream& ds, T (&v)[N] ) { /** * Deserialize a fixed size C array of primitive type * - * @brief Deserialize a fixed size C array of primitive type * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam T - Type of the object contained in the array @@ -682,7 +659,6 @@ datastream& operator >> ( datastream& ds, T (&v)[N] ) { /** * Serialize a vector of char * - * @brief Serialize a vector of char * @param ds - The stream to write * @param v - The value to serialize * @tparam Stream - Type of datastream buffer @@ -698,7 +674,6 @@ datastream& operator << ( datastream& ds, const std::vector& operator << ( datastream& ds, const std::vector& /** * Deserialize a vector of char * - * @brief Deserialize a vector of char * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -734,7 +708,6 @@ datastream& operator >> ( datastream& ds, std::vector& v ) /** * Deserialize a vector * - * @brief Deserialize a vector * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -754,7 +727,6 @@ datastream& operator >> ( datastream& ds, std::vector& v ) { /** * Serialize a set * - * @brief Serialize a set * @param ds - The stream to write * @param s - The value to serialize * @tparam Stream - Type of datastream buffer @@ -774,7 +746,6 @@ datastream& operator << ( datastream& ds, const std::set& s ) /** * Deserialize a set * - * @brief Deserialize a set * @param ds - The stream to read * @param s - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -797,7 +768,6 @@ datastream& operator >> ( datastream& ds, std::set& s ) { /** * Serialize a map * - * @brief Serialize a map * @param ds - The stream to write * @param m - The value to serialize * @tparam Stream - Type of datastream buffer @@ -817,7 +787,6 @@ datastream& operator << ( datastream& ds, const std::map& m /** * Deserialize a map * - * @brief Deserialize a map * @param ds - The stream to read * @param m - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -841,7 +810,6 @@ datastream& operator >> ( datastream& ds, std::map& m ) { /** * Serialize a tuple * - * @brief Serialize a tuple * @param ds - The stream to write * @param t - The value to serialize * @tparam Stream - Type of datastream buffer @@ -859,7 +827,6 @@ datastream& operator<<( datastream& ds, const std::tuple& operator>>( datastream& ds, std::tuple& t ) /** * Serialize a class * - * @brief Serialize a class * @param ds - The stream to write * @param v - The value to serialize * @tparam DataStream - Type of datastream @@ -895,7 +861,6 @@ DataStream& operator<<( DataStream& ds, const T& v ) { /** * Deserialize a class * - * @brief Deserialize a class * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam DataStream - Type of datastream @@ -913,7 +878,6 @@ DataStream& operator>>( DataStream& ds, T& v ) { /** * Serialize a primitive type * - * @brief Serialize a primitive type * @param ds - The stream to write * @param v - The value to serialize * @tparam Stream - Type of datastream buffer @@ -929,7 +893,6 @@ datastream& operator<<( datastream& ds, const T& v ) { /** * Deserialize a primitive type * - * @brief Deserialize a primitive type * @param ds - The stream to read * @param v - The destination for deserialized value * @tparam Stream - Type of datastream buffer @@ -946,7 +909,6 @@ datastream& operator>>( datastream& ds, T& v ) { * Unpack data inside a fixed size buffer as T * * @ingroup datastream - * @brief Unpack data inside a fixed size buffer as T * @tparam T - Type of the unpacked data * @param buffer - Pointer to the buffer * @param len - Length of the buffer @@ -964,7 +926,6 @@ T unpack( const char* buffer, size_t len ) { * Unpack data inside a variable size buffer as T * * @ingroup datastream - * @brief Unpack data inside a variable size buffer as T * @tparam T - Type of the unpacked data * @param bytes - Buffer * @return T - The unpacked data @@ -978,7 +939,6 @@ T unpack( const std::vector& bytes ) { * Get the size of the packed data * * @ingroup datastream - * @brief Get the size of the packed data * @tparam T - Type of the data to be packed * @param value - Data to be packed * @return size_t - Size of the packed data @@ -994,7 +954,6 @@ size_t pack_size( const T& value ) { * Get packed data * * @ingroup datastream - * @brief Get packed data * @tparam T - Type of the data to be packed * @param value - Data to be packed * @return bytes - The packed data From eef570aec82fec1059f95b38f6c36bc2c548e56f Mon Sep 17 00:00:00 2001 From: iamveritas Date: Fri, 12 Jun 2020 21:21:22 +0300 Subject: [PATCH 6/7] updates based on code PR review comments --- docs/05_best-practices/09_deferred_transactions.md | 4 ++-- .../how_to_restrict_access_to_an_action_by_user.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/05_best-practices/09_deferred_transactions.md b/docs/05_best-practices/09_deferred_transactions.md index c2d4a7eb11..7866fed4cd 100644 --- a/docs/05_best-practices/09_deferred_transactions.md +++ b/docs/05_best-practices/09_deferred_transactions.md @@ -6,7 +6,7 @@ Deferred communication conceptually takes the form of action notifications sent As already mentioned, deferred communication will get scheduled later at the producer's discretion. From the perspective of the originating transaction, i.e., the transaction that creates the deferred transaction, it can only determine whether the create request was submitted successfully or whether it failed (if it fails, it will fail immediately). Deferred transactions carry the authority of the contract that sends them. A transaction can cancel a deferred transaction. -Because all of the above it is not recommended to use `deferred transactions`. - [[warning | Deferred Transactions Are Deprecated]] | As of [EOSIO 2.0 RC1](https://github.com/EOSIO/eos/releases/tag/v2.0.0-rc1) deferred transactions are deprecated. + +Due to the above described behaviors it is not recommended to use `deferred transactions`. diff --git a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md index 4002a87446..3797a7ce61 100644 --- a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md +++ b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md @@ -5,7 +5,7 @@ link_text: How To Perform Authorization Checks ## Preconditions -It is assumed the following: +The following conditions are assumed: 1. You have the sources of a contract with one of the actions defined, let's call it `hi` action. 2. The `hi` action has defined one input parameter `user` of type `name`. From 5cbd7862040c7654f68734bbe6f4f44415c2c512 Mon Sep 17 00:00:00 2001 From: iamveritas Date: Fri, 12 Jun 2020 22:08:13 +0300 Subject: [PATCH 7/7] got rid of the gerunds (-ing verb terminations) --- .../05_best-practices/05_securing_your_contract.md | 2 +- .../how_to_restrict_access_to_an_action_by_user.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/05_best-practices/05_securing_your_contract.md b/docs/05_best-practices/05_securing_your_contract.md index a69c136060..54a5db5ee5 100644 --- a/docs/05_best-practices/05_securing_your_contract.md +++ b/docs/05_best-practices/05_securing_your_contract.md @@ -4,7 +4,7 @@ content_title: Securing your contract ## Basic Recommendations -The following are basic recommendations which can be the foundation of securing your smart contract. +The following are basic recommendations which can be the foundation for securing your smart contract. ### 1. Authorization Checks diff --git a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md index 3797a7ce61..6e25e60c0f 100644 --- a/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md +++ b/docs/06_how-to-guides/05_authorization/how_to_restrict_access_to_an_action_by_user.md @@ -9,16 +9,16 @@ The following conditions are assumed: 1. You have the sources of a contract with one of the actions defined, let's call it `hi` action. 2. The `hi` action has defined one input parameter `user` of type `name`. -3. The `hi` action is printing the name of the `user` account. +3. The `hi` action prints the name of the `user` account. 4. The `hi` action needs to authorize the `user` account. ## Authorization Methods To restrict access to the `hi` action, you can do it in three ways. -### 1. Using eosio::check(eosio::has_auth(...)...) +### 1. Use eosio::check(eosio::has_auth(...)...) -The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account is using to sign the transaction (e.g. owner, active, code). +The below code enforces the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account uses to sign the transaction (e.g. owner, active, code). [[info | Error message is custom]] | Observe that in this case the yielded error message is a custom one and thus it can be used to provide a better experience for the user. @@ -34,9 +34,9 @@ void hi( name user ) { Another example can be found in the [Tic Tac Toe Tutorial](https://developers.eos.io/welcome/latest/tutorials/tic-tac-toe-game-contract/#action-handler---move). -### 2. Using require_auth +### 2. Use require_auth -The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account is using to sign the transaction (e.g. owner, active, code). +The below code enforces the action `hi` to be executed only by the account that is sent as parameter to the action, no matter what permission the account uses to sign the transaction (e.g. owner, active, code). ```cpp void hi( name user ) { @@ -48,9 +48,9 @@ void hi( name user ) { [[info | Error message is not custom]] | Note that this time you can not customize the yielded error message, it will be a generic authorization error message. -### 3. Using require_auth2 +### 3. Use require_auth2 -The below code is enforcing the action `hi` to be executed only by the account that is sent as parameter to the action and only if the permission used to sign the transaction is the 'active' one. In other words, if the same user is signing the transaction with a different permission (e.g. code, owner) the execution of the action is halted. +The below code is enforces the action `hi` to be executed only by the account that is sent as parameter to the action and only if the permission used to sign the transaction is the 'active' one. In other words, if the same user uses the transaction with a different permission (e.g. code, owner) the execution of the action is halted. ```cpp #include