Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Curso de OpenZeppelin

Curso de OpenZeppelin

Sebastián Leonardo Perez

Sebastián Leonardo Perez

Reto: control de acceso

5/19
Recursos

Reto: control de acceso

  1. Toma el contrato Storage de Remix.
  2. Implementa dos roles: “Admin” y “Writer”.
  3. El rol “Admin” podrá agregar y quitar “Writer”.
  4. El rol “Writer” será el único habilitado para utilizar la función “store”.
  5. La función receive no tendrá restricciones de rol.
  6. Utiliza modificadores.

Aportes 20

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Interesante la mejora para facilitar la adición de los roles.
Desafio completado en Hardhat

Si tienen duda de donde provienen las funciones como:

hasRole()
_grantRole()
_revokeRole()

Son funciones heradas del AccessControl.sol que pueden encontrar el contrato aqui
que necesitarias para una parte del reto.

Reto:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract Storage is AccessControl{

    bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER");

    uint256 number;

    constructor(){
        _grantRole( ROL_ADMIN, msg.sender );
    }

    function store(uint256 num) public {
        require( hasRole(ROL_WRITER, msg.sender), "Esta funcion solo puede ser utilizada por el WRITER" );
        number = num;
    }

    function retrieve() public view returns (uint256){
        return number;
    }

    modifier onlyAdmin(){
         require( hasRole( ROL_ADMIN, msg.sender ), "Esta funcion solo puede ser utilizada por el ADMIN" );

         _;
     }

    function agregarRol( bytes32 role, address account ) public onlyAdmin() {
        _grantRole( role, account );
    }
    
    function quitarRol( bytes32 role, address account ) public onlyAdmin() {
        _revokeRole( role, account );
    }

}

Reto completado

He usado herencia y la librería de Access Control para el Ownable y Roles.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.8 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "../contracts/1_Storage.sol";


contract primerRetoOpenZeppelin is Ownable, AccessControl, Storage {

    // Constantes en bytes32 incluiendo encriptacion para la asignación de roles predefinidos.
    // Constants in bytes32, including encryption for predefined role assignment.

    bytes32 public constant ADMIN_ROL = keccak256("Admin_rol");
    bytes32 public constant WRITER_ROL = keccak256("Writer_rol");

    // Instancia de constructor, para asignar por defecto un rol administrativo al contrato.
    // Cuando se utiliza Ownable, el constructor predeterminado define que la cuenta utilizada es el propietario que implementa el contrato.
    // Constructor instance, to assign an administrative role to the default contract.
    // When Ownable is used, the default constructor defines that the account used is the owner that implements the contract.
    constructor(){
        _grantRole(ADMIN_ROL, msg.sender);
    }

    //Modifiers belonging to the ADMIN and WRITER roles.
    //Modificadores pertenencientes a los roles ADMIN y ESCRITOR.
    modifier AdminRole(){
        require(hasRole(ADMIN_ROL,msg.sender), "Function valid for the Contract Administrator");
        _;
    }
    modifier WriterRole(){
        require(hasRole(WRITER_ROL,msg.sender), "Function valid for the Writer of the contract");
        _;
    }

    // Required feature for the challenge, add writer and remove writer.
    // Funcion requeridas para el reto, agregar escritor, y remover escritor.

    function addWriter(bytes32 role, address account) 
    public 
    AdminRole
    {
        _grantRole(role,account);
    }

    function RemoveWriter(bytes32 role, address account) 
    public 
    AdminRole
    {
        _revokeRole(role,account);
    }

    function store(uint256 num) 
    public override
    WriterRole
    {
        number = num;
    }

}

Los requerimientos del reto están en el contrato, añadiendo otros plus como:

  • Librerías: Access Control Ownable, AccessControl.

  • Convierte en el propietario del contrato a quien lo despliega.

  • Transferir a otro ownership y renunciar automaticamente.

  • Renunciar al contrato pone como owner 0 al contrato. Aunque se puede seguir teniendo rol de ADM.

  • El rol ADM se pone por defecto al desplegar el contrato.

  • El ADM puede agregar Role Writer y remover.

  • El Writer puede accionar a la función Store.

  • La función receive es accesible por todos.

Esta es mi solución al reto, usando un Enum con las acciones Grant y Revoke para tener una sola función al momento de agregar o quitar el rol Writer.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage is AccessControl{

    enum RoleAction { Grant, Revoke}

    bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER");

    uint256 number;

    modifier onlyWriter() {
        require(hasRole(ROL_WRITER,msg.sender),"Only WRITER can call this function");
        _;
    }

    modifier onlyAdmin() {
        require(hasRole(ROL_ADMIN,msg.sender),"Only ADMIN can call this function");
        _;
    }

    constructor() {
        _grantRole(ROL_ADMIN, msg.sender);
    }

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyWriter{
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }

    function modifyWriterRole(address account, RoleAction actionToDo) public onlyAdmin{
        if(actionToDo == RoleAction.Grant){
            _grantRole(ROL_WRITER,account);
        }
        else if(actionToDo == RoleAction.Revoke){
            _revokeRole(ROL_WRITER,account);
        }
    }
}

Este es mi desafío, lo quise hacer también con herencia para aplicar los conceptos aprendidos en los anteriores cursos:
Aclaración: Para esto tuve que modificar la función store del código de 1_Storage.sol, agregarle virtual para luego poder agregarle el modifier:

  • 1_Storage.sol:
// Único cambio aplicado al contrato base:
function store(uint256 num) public virtual {
        number = num;
    }
  • Solución del reto en el contrato StorageChallenge.sol::
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "./1_Storage.sol";

contract StorageChallenge is AccessControl, Storage {

    bytes32 public constant ADMIN = keccak256("ADMIN");
    bytes32 public constant WRITER = keccak256("WRITER");

    constructor() {
        _grantRole(ADMIN, msg.sender);
    }

    modifier isAdmin() {
        require(hasRole(ADMIN, msg.sender), "Only the admin can  access to this function");
        _;
    }

    modifier isWriter() {
        require(hasRole(WRITER, msg.sender), "Only the WRITER can  access to this function");
        _;
    }

    function giveRole(address _writer) external isAdmin {
        _grantRole(WRITER, _writer);
    }

    function revokeRole(address _writer) external isAdmin {
        _revokeRole(WRITER, _writer);
    }

    function store(uint256 num) public override isWriter {}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract Storage is AccessControl {

    bytes32 public constant ADMIN = keccak256("ADMIN");
    bytes32 public constant WRITER = keccak256("WRITER");   

    uint256 number;

    constructor(){
        _setupRole(ADMIN, msg.sender);
    }

    modifier functionAccess ( bytes32 _role ) {
        require(
            hasRole( _role , msg.sender),
            "Sender not authorized"
        );
        _;
    }


    function addRol( address account  ) public functionAccess( ADMIN ) {
        _grantRole(WRITER, account);
    }

    function revokeRol( address account ) public functionAccess( ADMIN ) {
        _revokeRole(WRITER, account);
    }
    
    function store(uint256 num) public functionAccess( WRITER ) {
        number = num;
    }

    function retrieve() public view returns (uint256){
        return number;
    }
}```

Reto:

<// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    bytes32 public constant WRITER_ROLE = keccak256("WRITER_ROLE");
    uint256 number;

    constructor() {
        _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
        _setRoleAdmin(WRITER_ROLE, ADMIN_ROLE);

        _grantRole(ADMIN_ROLE, _msgSender());
        _grantRole(ADMIN_ROLE, address(this));
    }

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyRole(WRITER_ROLE) {
        number = num;
    }

    /**
     * @dev Return value
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256) {
        return number;
    }
}
> 

Aqui solución del reto, sin override del contrato Storage:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "./1_Storage.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract ContratoConRoles is AccessControl, Storage {

    bytes32 public constant rolAdmin = keccak256("ROL_ADMIN");
    bytes32 public constant rolWriter = keccak256("ROL_WRITER");

    modifier onlyWriter() {
        require(hasRole(rolWriter,msg.sender),"Only WRITER can call this function");
        _;
    }

    modifier onlyAdmin() {
        require(hasRole(rolAdmin,msg.sender),"Only ADMIN can call this function");
        _;
    }
    constructor() {
        _grantRole(rolAdmin, msg.sender);
    }


    function agregarRol(address account) public onlyAdmin{
       _grantRole(rolWriter,account);
    }
    
    function quitarRol(address account) public onlyAdmin{
       _revokeRole(rolWriter,account);
    }
    function agregarStorage(uint256 num) public onlyWriter{
        store (num);
    }
    
    function recuperarStorage() public view returns (uint256){
        return retrieve();
    }
}

Reto:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage is AccessControl{

    uint256 number;
    bytes32 constant public ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 constant public ROL_WRITER = keccak256("ROL_WRITER");
    
    constructor(){
        _grantRole(ROL_ADMIN, msg.sender);
    }

    modifier onlyAdmin(){
        require(hasRole(ROL_ADMIN, msg.sender),"You dont have access of admin");
        _;
    }

    modifier onlyWriter(){
        require(hasRole(ROL_WRITER, msg.sender),"You dont have access of writer");
        _;
    }
    
    function addWriterRole(address newWriter) public onlyAdmin{
        _grantRole(ROL_WRITER,newWriter);
    }

    function removeWriterRole(address removeWriter) public onlyAdmin{
        _revokeRole(ROL_WRITER,removeWriter);
    }

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyWriter{
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}

Mi implementación utilizando el modificador onlyRole() que se encuentra en AccessControl.sol:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "oz-access/AccessControl.sol";

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage is AccessControl {

    bytes32 public Admin = keccak256("ADMIN");
    bytes32 public Writer = keccak256("WRITER");

    uint256 number;

    constructor () {
      _grantRole(Admin, msg.sender);
    }

    /// @notice grants Writer role to newWriter, only an admin can do this
    function addWriter(address newWriter) public onlyRole(Admin) {
      _grantRole(Writer, newWriter);
    }

    /// @notice revokes Writer role to exWriter, only an admin can do this
    function removeWriter(address exWriter) public onlyRole(Admin) {
      _revokeRole(Writer, exWriter);
    }

    /**
     * @dev Store value in variable, only a writer can do this
     * @param num value to store
     */
    function store(uint256 num) public onlyRole(Writer) {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract Storage is AccessControl{

    bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER");
    uint256 number;

    constructor(){
        _grantRole( ROL_ADMIN, msg.sender );
    }

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) IsWriter() public {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }


    // Ya que el reto no implica crear roles de manera dinamica solo uso el parametro account
    function addWriter(address account) public IsAdmin(){
        _grantRole(ROL_WRITER, account);
    }

    function deleteWriter(address account) public {
        _revokeRole(ROL_WRITER, account);
    }

    modifier IsAdmin(){
        require(hasRole(ROL_ADMIN, msg.sender), "this function require ROL_ADMIN");
        _;
    }

    modifier IsWriter(){
        require(hasRole(ROL_ADMIN, msg.sender), "this function require ROL_WRITER");
        _;
    }


}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import “@openzeppelin/contracts/access/AccessControl.sol”;

contract Reto is AccessControl {
uint256 contador;
bytes32 rolAdmin = keccak256(“ROL_ADMIN”);
bytes32 rolEscritor = keccak256(“ROL_ESCRITOR”);

constructor() {
    contador = 10;
    _grantRole(rolAdmin, msg.sender);
}

modifier soloEscritura() {
    require(
        hasRole(rolEscritor, msg.sender),
        "Debe tener el rol de escritura para ejecutar la funcion"
    );
    _;
}

modifier soloAdmin() {
    require(
        hasRole(rolAdmin, msg.sender),
        "Debe tener rol de admin para ejecutar la funcion"
    );
    _;
}

function asignarContador(uint256 numero) public soloEscritura {
    contador = numero;
}

function asignarEscritor(address usuario) public soloAdmin {
    _grantRole(rolEscritor, usuario);
}

function eliminarEscritor(address usuario) public soloAdmin {
    _revokeRole(rolEscritor, usuario);
}

}

Buen ejercicio creo que por fin entendí para que sirve el _setRoleAdmin lo único que aun no me queda
del todo claro es si es correcto hacer _setRoleAdmin(_setRoleAdmin, _setRoleAdmin) que admin sea su
propio admin para que pueda pasar los controles cuando se usa

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/access/AccessControl.sol";

contract Storage is AccessControl {
    bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER");
    uint256 number;

    constructor() {
        _grantRole(ROL_ADMIN, _msgSender());
        _setRoleAdmin(ROL_WRITER, ROL_ADMIN);
    }

    function addRoleWriter(address account) public onlyRole(getRoleAdmin(ROL_WRITER)) {
        _grantRole(ROL_WRITER, account);
    }

    function removeRoleWriter(address account) public onlyRole(getRoleAdmin(ROL_WRITER)) {
        _revokeRole(ROL_WRITER, account);
    }

    function store(uint256 num) public onlyRole(ROL_WRITER){
        number = num;
    }

    function retrieve() public view returns (uint256){
        return number;
    }
}

Lo habia logrado sin modificadores, cuando vi el video dije, upss se me olvido XD

Hola en un principio me quedó así, le agregue la función de cambiar admin, por el momento parece funcionar, si encuentran algún error me dice XD

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract ControlAccess is AccessControl{
    bytes32 ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 ROL_WRITER = keccak256("ROL_WRITER");


    constructor(){
        _grantRole(ROL_ADMIN,msg.sender);
    }

    uint256 number;

    function setWriter(address direccion) public onlyAdmin{
        
        _grantRole(ROL_WRITER,direccion);

    }
    function revokeWriter(address direccion) public onlyAdmin{
        
        _revokeRole(ROL_WRITER,direccion);

    }
    function changeAdmin(address nuevoAdmin) public onlyAdmin{
        _revokeRole(ROL_ADMIN,msg.sender);
        _grantRole(ROL_ADMIN,nuevoAdmin);
    }

    modifier onlyAdmin{
        require(hasRole(ROL_ADMIN,msg.sender), "No eres admin, Sal de ahi!");
        _;
    }
    modifier onlyWriter{
        require(hasRole(ROL_WRITER,msg.sender), "No eres Writer, Sal de ahi!");
        _;
    }

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyWriter{
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}

Me encanto el desafío

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract RetroControlAccesso is AccessControl {

    bytes32 public constant ROL_ADMIN = keccak256("ROL_ADMIN");
    bytes32 public constant ROL_WRITER = keccak256("ROL_WRITER");

    constructor() {
        _grantRole(ROL_ADMIN, msg.sender);
    }

    modifier onlyAdmin  {
        require(hasRole(ROL_ADMIN, msg.sender),"Esta funcion solo puede ser utilizada por el rol ADMIN");
        _;
    }

    modifier onlyWriter {
        require(hasRole(ROL_WRITER, msg.sender),"Esta funcion solo puede ser utilizada por el rol WRITER");
        _;
    }

    function agregarRolWriter(address account) public onlyAdmin {
        _grantRole(ROL_WRITER,account);
    }

    function quitarRolWriter(address account) public onlyAdmin {
        _revokeRole(ROL_WRITER,account);
    }
}


/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage is RetroControlAccesso {

    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyWriter {
         number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */

import "@openzeppelin/contracts/access/AccessControl.sol";

contract Storage is AccessControl {

    bytes32 rolAdmin = keccak256("ROL_ADMIN");
    bytes32 rolWriter = keccak256("ROL_WRITER");

    constructor() {
        _grantRole(rolAdmin, msg.sender);
    }

    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public onlyWriter{
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }

    
    modifier onlyWriter() {
        require(hasRole(rolWriter, msg.sender), "Solo pueden ejecutar la funcion los roles Writer");
        _;
    }

    modifier onlyAdmin() {
        require(hasRole(rolAdmin, msg.sender), "Solo pueden ejecutar la funcion los roles Admin");
        _;
    }

    function agregarWriter(address cuenta) public onlyAdmin {
        _grantRole(rolWriter, cuenta);
    }

    function quitarWriter(address cuenta) public onlyAdmin {
        _revokeRole(rolWriter, cuenta);
    }




}

Me encanto esta clase!!