Understanding Multi-Signature Wallet Technology

Understanding Multi-Signature Wallet Technology

In the realm of digital transactions, security is paramount. As traditional methods evolve into digital formats, ensuring the integrity of transactions becomes increasingly complex. One innovative solution gaining traction is multi-signature technology, offering heightened security and trust in digital dealings.

What is Multi-Signature Technology?

Multi-signature, often abbreviated as multisig, is a cryptographic technique that requires multiple parties to sign a transaction before it can be executed. Unlike traditional single-signature transactions, where only one party is needed to authorize a transaction, multi-signature technology mandates the involvement of multiple signatories.

A multisig wallet is like a bank's safe deposit box that requires multiple keys to be used at the same time to open it.

Multisig wallets are based on the idea of smart contracts. The wallet is governed on the blockchain by its owners. It also automatically executes transactions when certain predetermined conditions are met (all the signatures are provided) and without the involvement of any third-party intermediary.

Because of these design considerations, multisig wallets are considered a type of "seedless self-custody" of cryptocurrencies. Self-custody lets crypto owners manage their own crypto assets. Seedless means that there's no need for a seed phrase to recover a lost or stolen bitcoin wallet. Instead, the wallet provides additional security by distributing key generation and signature among multiple parties

Benefits of Multi-Signature Technology

1. Enhanced Security: By requiring multiple signatures, multi-signature technology reduces the risk of unauthorized transactions or fraud. Even if one key is compromised, the transaction cannot proceed without additional authorized signatures.

2. Risk Distribution: Multi-signature arrangements distribute control among multiple parties, mitigating the risk of a single point of failure. This is particularly advantageous in scenarios involving valuable assets or sensitive information.

3. Trustless Transactions: Multi-signature technology enables trustless transactions, eliminating the need for intermediaries or third-party escrow services. This fosters greater autonomy and efficiency in digital dealings.

Applications of Multi-Signature Technology

1. Cryptocurrency Wallets: Multi-signature wallets are increasingly popular in the realm of cryptocurrency, where they offer an additional layer of security against theft or hacking.

2. Smart Contracts: In decentralized applications (DApps) and blockchain platforms like Ethereum, multi-signature functionality can be incorporated into smart contracts to govern transactions and agreements.

3. Corporate Governance: Multi-signature technology finds utility in corporate governance structures, where multiple stakeholders are required to authorize significant decisions or transactions.

How Does Multi-Signature Work?

Multisig, also called multi-signature, is the requirement for a transaction to have two or more signatures before it can be executed. Multisig provides more security than single-signature transactions.

In a multisig transaction, at least two signatures are required to approve a transaction. The idea is useful when more than one party has ownership and all parties are needed to execute the transaction. This way, no one party can execute a transaction without the other parties agreeing to it, ensuring greater transparency and security for all parties.

Multisig transactions are also referred to as M-of-N transactions, with M being the required number of signatures or keys and N being the total number of signatures or keys involved in the transaction.

In a multi-signature setup, a predetermined number of signatures are required to validate a transaction. This predetermined number is established during the creation of the multi-signature wallet or contract. For example, a 2-of-3 multisig requires two out of three possible signatories to approve a transaction.

Here is an example of how multisig contract is implemented using Solidity:

contract MultiSig {
    address public owner;
    address[] public signers;
    uint256 public requiredNumOfSigners;
    uint256 public txCount;

    address public nextOwner;

    struct Transaction {
        uint256 id;
        uint256 amount;
        address receiver;
        uint256 signersCount;
        bool isExecuted;
        address txCreator;
    }

    Transaction[] public allTransactions;

    // Mapping of transaction id to signer address returning bool:
    mapping(uint256 => mapping(address => bool)) public hasSigned;

    mapping(address => bool) public isValidSigner;
}
}

here's an explanation of each line of code in the MultiSig contract:

  1. address public owner;: Declares a public state variable owner of type address, which will store the address of the contract owner.

  2. address[] public signers;: Declares a public state variable signers of type array of addresses, which will store the addresses of all signers involved in the multi-signature process.

  3. uint256 public requiredNumOfSigners;: Declares a public state variable requiredNumOfSigners of type uint256, which represents the minimum number of signers required to approve a transaction.

  4. uint256 public txCount;: Declares a public state variable txCount of type uint256, which represents the total count of transactions submitted to the contract.

  5. address public nextOwner;: Declares a public state variable nextOwner of type address, which represents the next proposed owner of the contract.

  6. struct Transaction { ... }: Defines a custom data structure named Transaction, which encapsulates information about each transaction, including its ID, amount, receiver, number of signers, execution status, and creator's address.

  7. Transaction[] public allTransactions;: Declares a public state variable allTransactions of type array of Transaction, which stores details of all transactions submitted to the contract.

  8. mapping(uint256 => mapping(address => bool)) public hasSigned;: Declares a nested mapping named hasSigned, which maps a transaction ID to a signer's address and returns a boolean value indicating whether the signer has approved the transaction.

  9. mapping(address => bool) public isValidSigner;: Declares a mapping named isValidSigner, which maps an address to a boolean value indicating whether the address is a valid signer authorized to approve transactions.

  contract MultiSig {

    //the above code varables declared
    constructor(address[] memory _validSigners, uint256 _requiredNumOfSigners) {
        owner = msg.sender;
        signers = _validSigners;
        requiredNumOfSigners= _requiredNumOfSigners;

        for (uint256 i = 0; i < _validSigners.length; i++) {
            isValidSigner[_validSigners[i]] = true;
        }
    }
}

Here's a breakdown of the constructor function in the MultiSig contract:

  1. constructor(address[] memory _validSigners, uint256 _requiredNumOfSigners): Defines the constructor function, which is executed only once during contract deployment. It takes two parameters: _validSigners, an array of addresses representing the valid signers, and _requiredNumOfSigners, an integer specifying the required number of signers for transaction approval.

  2. owner = msg.sender;: Assigns the address of the sender (the account deploying the contract) to the owner variable, indicating that the deployer initially owns the contract.

  3. signers = _validSigners;: Initializes the signers array with the addresses provided in the _validSigners parameter, representing the list of valid signers for multi-signature transactions.

  4. requiredNumOfSigners = _requiredNumOfSigners;: Sets the requiredNumOfSigners variable to the value specified in the _requiredNumOfSigners parameter, indicating the minimum number of signers required to approve a transaction.

  5. for (uint256 i = 0; i < _validSigners.length; i++) { ... }: Iterates through the _validSigners array to initialize the isValidSigner mapping, setting the value to true for each signer address. This ensures that only authorized signers can participate in the multi-signature process.

  6. isValidSigner[_validSigners[i]] = true;: Sets the value of the isValidSigner mapping to true for each signer address provided in the _validSigners array.


   contract MultiSig {
    //The code varables declared goes here...

    //The constructor function goes here...

    function initiateTransaction(uint256 _amount, address _receiver) external {
    // Check if the sender is a valid signer
    require(isValidSigner[msg.sender], "not valid signer");

    // Validate input parameters
    require(msg.sender != address(0), "Zero address detected");
    require(_amount > 0, "no zero value allowed");

    // Increment transaction count to generate unique ID
    uint256 _txId = txCount + 1;

    // Create a new transaction and store it in the allTransactions array
    Transaction storage tns = allTransactions[_txId];
    tns.id = _txId;
    tns.amount = _amount;
    tns.receiver = _receiver;
    tns.signersCount = 1;
    tns.txCreator = msg.sender;

    // Add the sender to the list of signers for this transaction
    hasSigned[_txId][msg.sender] = true;

    // Increment transaction count
    txCount++;
    }
}
  1. function initiateTransaction(uint256 _amount, address _receiver) external: Declares a public function named initiateTransaction that allows external callers to initiate a transaction. It takes two parameters: _amount, representing the amount of Ether to be transferred in the transaction, and _receiver, specifying the address of the recipient.

  2. require(isValidSigner[msg.sender], "not valid signer");: Checks if the caller of the function (msg.sender) is a valid signer by verifying their address against the isValidSigner mapping. If the sender is not a valid signer, the function reverts with an error message.

  3. require(msg.sender != address(0), "Zero address detected");: Ensures that the sender's address is not the zero address, indicating a valid Ethereum address. If the sender's address is the zero address, the function reverts with an error message.

  4. require(_amount > 0, "no zero value allowed");: Validates that the transaction amount is greater than zero. If the amount is zero or negative, the function reverts with an error message.

  5. uint256 _txId = txCount + 1;: Generates a unique transaction ID by incrementing the txCount variable and assigning it to _txId.

  6. Transaction storage tns = allTransactions[_txId];: Declares a storage reference variable tns of type Transaction, which represents the transaction to be initiated. It retrieves the transaction from the allTransactions array using the generated transaction ID _txId.

  7. Initializes the transaction details:

    • tns.id= _txId;: Sets the transaction ID.

    • tns.amount = _amount;: Sets the transaction amount.

    • tns.receiver = _receiver;: Sets the recipient address.

    • tns.signersCount = 1;: Sets the initial number of signers to 1, representing the initiator of the transaction.

    • tns.txCreator = msg.sender;: Records the address of the transaction creator.

  8. hasSigned[_txId][msg.sender] = true;: Marks the transaction as signed by the initiator (the sender of the transaction) by setting the value to true in the hasSigned mapping for the transaction ID and the sender's address.

  9. txCount++;: Increments the transaction count to maintain uniqueness for the next transaction.

 contract MultiSig {
    //The code varables declared goes here...

    //The constructor function goes here...

    //The function initiateTransaction goes here...

function approveTransaction(uint256 _txId) external {
    // Check if the sender is a valid signer
    require(isValidSigner[msg.sender], "not valid signer");

    // Validate input parameters
    require(msg.sender != address(0), "Zero address detected");
    require(_txId <= txCount, "Invalid transaction id");
    require(!hasSigned[_txId][msg.sender], "Can't sign twice");

    // Get the transaction object from storage
    Transaction storage tns = allTransactions[_txId];

    // Perform various checks before approving the transaction
    require(address(this).balance >= tns.amount, "Insufficient contract balance");
    require(!tns.isExecuted, "transaction already executed");
    require(tns.signersCount < requiredNum, "required number reached");

    // Increment the signers count for this transaction
    tns.signersCount++;

    // Mark the sender as having signed this transaction
    hasSigned[_txId][msg.sender] = true;

    // If enough signers have approved the transaction, execute it
    if (tns.signersCount == requiredNum) {
        tns.isExecuted = true;
        payable(tns.receiver).transfer(tns.amount);
    }
}
  1. function approveTransaction(uint256 _txId) external {: Declares a public external function named approveTransaction that allows any external account to approve a transaction by providing the transaction ID (_txId) as a parameter.

  2. The function starts with several require statements to validate the conditions under which the transaction can be approved. These include ensuring that the sender is a valid signer, the transaction ID is valid, the sender's address is not the zero address, and the sender has not already signed the transaction.

  3. Transaction storage tns = allTransactions[_txId];: Retrieves the transaction object associated with the provided transaction ID from the allTransactions array and assigns it to a storage reference variable tns.

  4. Various checks are performed to ensure that the transaction can be approved:

    • Sufficient balance exists in the contract to cover the transaction amount.

    • The transaction has not already been executed.

    • The required number of signers has not been reached.

  5. If all checks pass, the function increments the signers count for the transaction and marks the sender as having signed it.

  6. If the required number of signers have approved the transaction, it is executed by transferring the specified amount of Ether (tns.amount) to the designated recipient (tns.receiver) using the transfer function. Additionally, the isExecuted flag of the transaction is set to true to prevent duplicate executions.

Link to the full code: https://github.com/Holamite/MultiSig_wallet/tree/main

Why use a multisig wallet?

A multisig crypto wallet lets crypto users avoid the problems caused by the loss of a single private key. Thus, even if a key is lost or stolen, the crypto and underlying funds in the wallet remain safe since the other keys can be used to operate the wallet.

The need for multiple signatures also means a hacker would need access to all the private keys to access an owner's funds. Since this is difficult to do, it's unlikely that they will be able to authorize transactions without the owner's knowledge or permission. To this extent, a multisig wallet functions like two-factor authentication, providing greater security for crypto owners.

Can I be scammed with a multisig wallet?

Yes, attackers can use a multisig wallet to deceive the victim.

The scam works like this:

  • The victim purchases cryptocurrency, likely for a “too good to be true” price.

  • The funds are sent to a multisig wallet that the victim does not own but has access. For example, the attacker may set up a 1-2 multisig wallet that lets the attacker move the funds without the victim's consent.

  • The attackers move the funds to a different wallet - one which the victim does not have access to.

My recommendations:

  • If you are purchasing crypto, please make sure the wallet you are going to receive the funds in is a wallet that you own, a wallet that you have created that is not a multisig wallet and only you have access to it.

  • If you are purchasing crypto, beware of “too good to be true” pricing or someone offering cryptocurrency at a much lower price than the current rate.

Challenges and Considerations:

1. Key Management: Managing multiple private keys can be complex and cumbersome, necessitating robust key management practices to prevent loss or compromise.

2. User Experience: The implementation of multi-signature technology should prioritize user experience, ensuring that the authentication process remains intuitive and accessible.

3. Scalability: As multi-signature transactions involve multiple signatures, scalability issues may arise, particularly in high-volume transaction environments.

Conclusion

Multi-signature technology represents a significant advancement in enhancing security and trust in digital transactions. By requiring multiple signatures to authorize transactions, multi-signature setups offer resilience against fraud, decentralization of control, and trustless interactions. As the digital landscape continues to evolve, multi-signature technology is poised to play a pivotal role in safeguarding the integrity of digital transactions.

Don't leave me, take me with you Like what you read? Follow me on social media to know more about EVM, blockchain development, and shit-posting.

Twitter: Adebara Olamide

Instagram: @holamite1

Reference