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:
address public owner;
: Declares a public state variableowner
of typeaddress
, which will store the address of the contract owner.address[] public signers;
: Declares a public state variablesigners
of type array of addresses, which will store the addresses of all signers involved in the multi-signature process.uint256 public requiredNumOfSigners;
: Declares a public state variablerequiredNumOfSigners
of typeuint256
, which represents the minimum number of signers required to approve a transaction.uint256 public txCount;
: Declares a public state variabletxCount
of typeuint256
, which represents the total count of transactions submitted to the contract.address public nextOwner;
: Declares a public state variablenextOwner
of typeaddress
, which represents the next proposed owner of the contract.struct Transaction { ... }
: Defines a custom data structure namedTransaction
, which encapsulates information about each transaction, including its ID, amount, receiver, number of signers, execution status, and creator's address.Transaction[] public allTransactions;
: Declares a public state variableallTransactions
of type array ofTransaction
, which stores details of all transactions submitted to the contract.mapping(uint256 => mapping(address => bool)) public hasSigned;
: Declares a nested mapping namedhasSigned
, which maps a transaction ID to a signer's address and returns a boolean value indicating whether the signer has approved the transaction.mapping(address => bool) public isValidSigner;
: Declares a mapping namedisValidSigner
, 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:
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.owner = msg.sender;
: Assigns the address of the sender (the account deploying the contract) to theowner
variable, indicating that the deployer initially owns the contract.signers = _validSigners;
: Initializes thesigners
array with the addresses provided in the_validSigners
parameter, representing the list of valid signers for multi-signature transactions.requiredNumOfSigners = _requiredNumOfSigners;
: Sets therequiredNumOfSigners
variable to the value specified in the_requiredNumOfSigners
parameter, indicating the minimum number of signers required to approve a transaction.for (uint256 i = 0; i < _validSigners.length; i++) { ... }
: Iterates through the_validSigners
array to initialize theisValidSigner
mapping, setting the value totrue
for each signer address. This ensures that only authorized signers can participate in the multi-signature process.isValidSigner[_validSigners[i]] = true;
: Sets the value of theisValidSigner
mapping totrue
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++;
}
}
function initiateTransaction(uint256 _amount, address _receiver) external
: Declares a public function namedinitiateTransaction
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.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 theisValidSigner
mapping. If the sender is not a valid signer, the function reverts with an error message.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.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.uint256 _txId = txCount + 1;
: Generates a unique transaction ID by incrementing thetxCount
variable and assigning it to_txId
.Transaction storage tns = allTransactions[_txId];
: Declares a storage reference variabletns
of typeTransaction
, which represents the transaction to be initiated. It retrieves the transaction from theallTransactions
array using the generated transaction ID_txId
.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.
hasSigned[_txId][msg.sender] = true;
: Marks the transaction as signed by the initiator (the sender of the transaction) by setting the value totrue
in thehasSigned
mapping for the transaction ID and the sender's address.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);
}
}
function approveTransaction(uint256 _txId) external {
: Declares a public external function namedapproveTransaction
that allows any external account to approve a transaction by providing the transaction ID (_txId
) as a parameter.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.Transaction storage tns = allTransactions[_txId];
: Retrieves the transaction object associated with the provided transaction ID from theallTransactions
array and assigns it to a storage reference variabletns
.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.
If all checks pass, the function increments the signers count for the transaction and marks the sender as having signed it.
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 thetransfer
function. Additionally, theisExecuted
flag of the transaction is set totrue
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
Buterin, V. (2013). Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform. Retrieved from https://github.com/ethereum/wiki/wiki/White-Paper
Adebara O. (2024) GitHub: A MultiSig contract https://github.com/Holamite/MultiSig_wallet