Pseudo Radom Number Generator with Signed message
An alternative way to do secure random on smart contract
Problems
Solidity is not capable of creating random numbers.
Don’t generate random numbers inside your contracts. It won’t work, you will get hacked.
Create your own’s algorithm for random numbers by readings blockchain env as a source of random the random number is 100% predictable and blockchain env can be manipulated
Approach 1:Simple Onchain random
pragma solidity ^0.8.0;contract Random {
function random() external view returns (uint256) {
uint256 blockNumber = block.number;
uint256 timeStamp = block.timestamp;
bytes32 blockHash = blockhash(block.number - 1);
return uint256(keccak256(abi.encodePacked(
blockNumber,
timeStamp,
blockHash)));
}
}
According to the source code, the random function is not hard enough to guess by attackers or malicious. And block timestamp can be manipulated by the miner in POW, but in POA is a bit different every node is trusted / honest, so the block timestamp can be predictable by the period of block time that has been set on genesis configuration.
Approach 2:Simple Off-chain random
pragma solidity ^0.8.0;contract Random {
function random(uint256 seed) external view returns (uint256) {
uint256 blockNumber = block.number;
uint256 timeStamp = block.timestamp;
bytes32 blockHash = blockhash(block.number - 1);
return uint256(keccak256(abi.encodePacked(
blockNumber,
timeStamp,
blockHash,
seed)));
}
}
Adding seed from off-chain is lack of trustworthiness and transparency, the project owner can be manipulated off-chain seed to enforce the result of random.
Approach 3:Combine On-chain and Off-chain
pragma solidity ^0.8.0;contract Random {
uint256 private _nounce; constructor(uint256 _initialNounce) {
_nounce = _initialNounce;
} function random(uint256 seed) external returns (uint256) {
_nounce += 1;
uint256 blockNumber = block.number;
uint256 timeStamp = block.timestamp;
bytes32 blockHash = blockhash(block.number - 1);
return uint256(keccak256(abi.encodePacked(
blockNumber,
timeStamp,
blockHash,
seed)));
}
}
In this case, The problem is private variable in Solidity doesn’t mean private, All data on a smart contract can be accessible. Accessing Private Data, Vulnerabilities of this contract are similar to an on-chain random smart contract. The attacker can predict by accessing private data, copy and pasting your random logic, and computing those things off-chain.
Currently Solution
Off-chain random
- Pros: Low-cost, Fast, and Effortless
- Cons: Lack of Transparency, Verifiable or Provable
Verifiable Randomness Function (VRF) Chainlink
- Pros: Transparency, Verifiable and Provable
- Cons: High-cost, Slow, and Effortful
Design Constraint
Cheap as much as possible.
Transparency, anyone can verify or prove.
Secured, hard to guess unpredictable patterns.
Question: Can we adopt a signed message for use as the seed of random?
Answer: Interesting, let’s find out.
What’s is Signed Message
Cryptographic signatures are a key part of the blockchain. They are used to prove ownership of an address without exposing its private key. This is primarily used for signing transactions but can also be used to sign arbitrary messages. Signed a message with a private key does not require interacting with the Ethereum network. It can be done completely offline.
The Magic of Digital Signatures on Ethereum
Setup and Run Project
Prerequisites
- node-js or nvm
- npm or yarn
git clone https://github.com/MASDXI/PRNG-contract.gitcd ./PRNG-contractyarn install
Contract interaction flow
- The Oracle signed random messages from the server-side to the client
- The Client signed a random message from client-side
- Client-side can call random by packing a signed message that came from Oracle/Off-chain source
- You can create a modifier to check if the attacker trying to create a fake oracle signed message for abuse or manipulate the random result
// Example modifier
mapping(address => bool)public oracle;modifier isOracle(address _signer) {
require(oracle[_signer],"invalid oracle address");
_;
}// OR
modifier isOracle(Entropy calldata _payload) {
address signer = Entropy.signer;
require(oracle[signer],"invalid oracle address");
_;
}
Compile and Test it on Remix IDE
Copy source code from Gist
Pseudo Radom Number Generator with Signed message
Enable SOLIDITY COMPILER
In ENVIRONMENT
option select JavaScript VM
Select contract PRNG.sol
Deploy PRNG
contract
Let’s try random with example data
Proving result by using data that came from a random event
Testing and Benchmark with Hardhat
for testing, contract run the following scripts
yarn testyarn coverageyarn benchmark
Conclusion
- You can create an efficient random number generator smart contract without using third-party oracle services like chainlink
- Raw data of Signed Message should be secret
- You can use a random set of dictionary words as sources for signs to make it much much hard to guess
- Oracle that’s signed message should have more than 1 account and rotate/change whenever random
Contents distributed by Learn.Block6.tech
👉 Discord — Live Talks
👉 Twitter — Latest articles