Solidity Tutorial: Understanding Design Patterns [Part 1]

Solidity Tutorial: Understanding Design Patterns [Part 1]
Profile picture of Tirtha Sarker Hacker Noon

Tirtha Sarker

Blockchain Developer. Quantum enthusiast

The decentralized world is growing exponentially in terms of user adoption. Companies create hundreds of innovative dApps on Ethereum every year to meet various market demands. At the heart of these dApps, we have EVM-enabled smart contracts, mostly written in Solidity. Although learning the syntax and solidity method is not difficult, but to build a scalable, accommodating, and secure contract, one needs to understand the proper design patterns.

picture

Why bother with design patterns?

Understanding is perceiving patterns
– Isaiah Berlin

One of the main differences between a noob coder and a professional developer is understanding the correct design patterns. These templates are proven solutions for common problems and can become your most powerful tool.

Compared to other software development verticals, programming robust smart contracts is a sophisticated task. The Distributed State Machine runs your deployed program 24/7, giving access to the whole world, it’s a double-edged sword. To ensure that your business logic runs securely, reliably, and deterministically, you should follow design best practices.

On a platform, where the slightest bug can cost you millions of dollars, would you dare to risk it?

picture

Soundness authorization models

Although smart contracts are easily accessible over the network by anyone, this may not be the desired goal of a program. Some functions can only be used by certain user groups.

Not handling this properly can lead to unpredictable use cases.

Some goals that authorization models can achieve:

  1. Authorize the execution of the post-authorization function by a group of users
  2. Authorize the execution of a transaction after authentication of the caller.
  3. Restrict the characteristics of a contract to the creator

I have identified 4 types of authorization models.

1. Access restriction

Concept:

Before any function logic can be executed, the caller must meet certain conditions. These conditions can be related to the identity of the caller, input parameters, contract status, etc. The function is executed after all the conditions are met.

If the requirements are not met, the EVM handles the error by inverting the state, without making any changes to the function call. If a similar restriction is needed for more than one function, a guard control can be used with modifiers. Additionally, if you do not want variables and functions to be publicly available, protect their state data by opting for private visibility.

Sample code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
 
   /**
    * @title An incremental time-bound donation receiver
    */
contract AccessRestriction {
   /**
    * @dev public variables in global, visible to everyone
    */
   address public treasury = msg.sender;
   uint256 public creationTime = block.timestamp;
   uint256 public minimumDonation;
   /**
    * @dev private visibility of winner address
    */
   address private winner;
 
   /**********Modifier Blocks********/
   /**
    * @dev check if donation period has started
    */
   modifier onlyBefore(uint256 _time) {
       require(block.timestamp < _time);
       _;
   }
 
    /**
    * @dev check if donation period has ended
    */
   modifier onlyAfter(uint256 _time) {
       require(block.timestamp > _time);
       _;
   }
 
   modifier isHigherDonation() {
       require(msg.value > minimumDonation, "Please send higher amount");
       winner = msg.sender;
       minimumDonation = msg.value;
       _;
   }
 
   function sendDonation()
       external
       payable
       onlyBefore(creationTime + 1 weeks)
       isHigherDonation
   {
       payable(treasury).transfer(msg.value);
   }
 
   function revealHighestDonor()
       external
       view
       onlyAfter(creationTime + 1 weeks)
       returns (address)
   {
       return winner;
   }
}

2. Multiple authorization

Concept:

There are situations where a transaction or a function call may require acknowledgment from multiple users. So if the authorizer pool is size X and we need a subset of Y, no. authorizations (where X≥Y), to execute the transaction. This is a useful approach when payment transactions based on multiple signatures are involved. The challenge with this approach is that the members of the authorization group must be predetermined and the minimum number of authorizers must be available during the execution period for signing. Likewise, if a key is compromised / lost, that authorizer account becomes useless.

picture

Sample code:

A simple implementation for multiple authorization that I found interesting was written by Christian Lundkvist. Check out its SimpleMultisig implementation here.

The relatively secure and audited baseline and implementation can be found in the Gnosis secure repository.

It is recommended that you do not use unaudited multi-authentication code for such model implementations.

3. Role-based ownership and access control

Concept:

A deployed contract can have dedicated role-based operation and access, just like in any traditional system. Role assignment and administration can be done by enforcing ownership and role-based access control rules. Role-based access can help design feature-specific modifiers that only allow the dedicated group.

Sample code:

The standard implementation of this template is available in the OpenZeppelin library.

4. Dynamic authorization activated by off-chain secret

Concept:

Unlike pre-authorized groups and role-based members, there may be situations where the authorized party is unknown for a given situation. Dynamic binding of an address is not performed by default, it must be allowed during the first transaction / contract deployment, as ACLs are usually predefined.

For such situation, we can generate off-chain secret on client side for dedicated function purpose. This secret will be hashed (SHA-256) and recorded on the contract. An operational binding will be made against the secret, so that any user who sends the transaction with the secret as a parameter, will be authorized. These secrets can be shared with a standard key exchange protocol with the addresses provided.

Hashlocks are useful for payment status channels, escrow atomic swaps. Moreover, secrets are the best for a transaction, as chain verification will reveal it.

picture

Sample code:

This is a simple secret recording that can be used with any hashlock. The secret is transmitted to the contract for disclosure and reference. For time-locked events, the reveal block and the expiration block can be compared.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
 
 
contract SecretRegistry {
   mapping(bytes32 => uint256) private secretToBlock;
 
 
   event SecretRevealed(bytes32 indexed secrethash, bytes32 secret);
  
   /**
    * @dev Register the secret that's been used for validation
    */
   function registerSecret(bytes32 secret) public returns (bool) {
       bytes32 secrethash = sha256(abi.encodePacked(secret));
       if (secretToBlock[secrethash] > 0) {
           return false;
       }
       secretToBlock[secrethash] = block.number;
       emit SecretRevealed(secrethash, secret);
       return true;
   }
 
 
   function getRevealedSecretBlockHeight(bytes32 secrethash) public view returns (uint256) {
       return secretToBlock[secrethash];
   }
 
}

End Notes

On-chain authorization through role and ownership management has become a necessity for contract-based asset management. Also, be extremely careful with off-chain secrets and how you share them. Try using one of these templates in your next dApp.

You can find the sample code snippets in this repository.

Oh look, Obi-wan has something to say

picture

In the next part of this series, I will talk about “Control models”.

References used:

  1. Design models for smart contracts in the Ethereum-Wohrer ecosystem, Zdun
  2. CSIRO Research
  3. OpenZepplin contractual documents
  4. Solidity: 0.8.9 documentation

Key words



Source link

Abdul J. Gaspar