diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz new file mode 100644 index 00000000..4940bd19 Binary files /dev/null and b/.yarn/install-state.gz differ diff --git a/Week19/randomness/contracts/PseudoRandom b/Week19/randomness/contracts/PseudoRandom new file mode 100644 index 00000000..1542284e --- /dev/null +++ b/Week19/randomness/contracts/PseudoRandom @@ -0,0 +1,102 @@ +//SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +contract PseudoRandom { + using ECDSA for bytes32; + + struct Signature { + uint8 v; + bytes32 r; + bytes32 s; + } + + Signature savedSig; + + function setSignature( + uint8 _v, + bytes32 _r, + bytes32 _s + ) public { + savedSig = Signature({v: _v, r: _r, s: _s}); + } + + function getRandomNumber(string calldata seed) + public + view + returns (uint256 pseudoRandomNumber) + { + address messageSigner = verifyString( + seed, + savedSig.v, + savedSig.r, + savedSig.s + ); + require(msg.sender == messageSigner, "Invalid seed"); + pseudoRandomNumber = uint256(keccak256(abi.encodePacked(seed))); + } + + function getCombinedRandomNumber(string calldata seed) + public + view + returns (uint256 pseudoRandomNumber) + { + address messageSigner = verifyString( + seed, + savedSig.v, + savedSig.r, + savedSig.s + ); + require(msg.sender == messageSigner, "Invalid seed"); + pseudoRandomNumber = uint256( + keccak256(abi.encodePacked(seed, blockhash(block.number - 1))) + ); + } + + // Don't worry about this function for now + function verifyString( + string memory message, + uint8 v, + bytes32 r, + bytes32 s + ) public pure returns (address signer) { + string memory header = "\x19Ethereum Signed Message:\n000000"; + uint256 lengthOffset; + uint256 length; + assembly { + length := mload(message) + lengthOffset := add(header, 57) + } + require(length <= 999999); + uint256 lengthLength = 0; + uint256 divisor = 100000; + while (divisor != 0) { + uint256 digit = length / divisor; + if (digit == 0) { + if (lengthLength == 0) { + divisor /= 10; + continue; + } + } + lengthLength++; + length -= digit * divisor; + divisor /= 10; + digit += 0x30; + lengthOffset++; + assembly { + mstore8(lengthOffset, digit) + } + } + if (lengthLength == 0) { + lengthLength = 1 + 0x19 + 1; + } else { + lengthLength += 1 + 0x19; + } + assembly { + mstore(header, lengthLength) + } + bytes32 check = keccak256(abi.encodePacked(header, message)); + return ecrecover(check, v, r, s); + } +} \ No newline at end of file diff --git a/Week19/randomness/contracts/Random.sol b/Week19/randomness/contracts/Random.sol new file mode 100644 index 00000000..3b3ac49c --- /dev/null +++ b/Week19/randomness/contracts/Random.sol @@ -0,0 +1,13 @@ +contract Random { + function getRandomNumber() + public + view + returns (uint256 randomNumber) + { + // TODO: get randomness from previous block randao reveal + } + + function tossCoin() public view returns (bool heads) { + // TODO: make the random number be translated to a boolean + } +} \ No newline at end of file diff --git a/Week19/randomness/scripts/TestPseudoRandom.ts b/Week19/randomness/scripts/TestPseudoRandom.ts new file mode 100644 index 00000000..ee799a7b --- /dev/null +++ b/Week19/randomness/scripts/TestPseudoRandom.ts @@ -0,0 +1,109 @@ +import { ethers } from "ethers"; + +async function signature() { + const signers = await ethers.getSigners(); + const signer = signers[0]; + console.log( + `Signing a message with the account of address ${signer.address}` + ); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.question("Enter a message to be signed:\n", async (answer) => { + const signedMessage = await signer.signMessage(answer); + console.log(`The signed message is:\n${signedMessage}`); + rl.close(); + testSignature(); + }); +} + +async function testSignature() { + console.log("Verifying signature\n"); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.question("Enter message signature:\n", (signature) => { + rl.question("Enter message:\n", (message) => { + const address = ethers.utils.verifyMessage(message, signature); + console.log(`This message signature matches with address ${address}`); + rl.question("Repeat? [Y/N]:\n", (answer) => { + rl.close(); + if (answer.toLowerCase() === "y") { + testSignature(); + } + }); + }); + }); +} + +async function sealedSeed() { + console.log("Deploying contract"); + const contractFactory = await ethers.getContractFactory("PseudoRandom"); + const contract: PseudoRandom = await contractFactory.deploy(); + await contract.deployed(); + const signers = await ethers.getSigners(); + const signer = signers[0]; + console.log( + `Signing a message with the account of address ${signer.address}` + ); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.question("Enter a random seed to be signed:\n", async (seed) => { + const signedMessage = await signer.signMessage(seed); + rl.close(); + console.log(`The signed message is:\n${signedMessage}`); + const sig = ethers.utils.splitSignature(signedMessage); + console.log("Saving signature at contract"); + await contract.setSignature(sig.v, sig.r, sig.s); + try { + console.log("Trying to get a number with the original seed"); + const randomNumber = await contract.getRandomNumber(seed); + console.log(`Random number result:\n${randomNumber}`); + console.log("Trying to get a number without the original seed"); + const fakeSeed = "FAKE_SEED"; + const randomNumber2 = await contract.getRandomNumber(fakeSeed); + console.log(`Random number result:\n${randomNumber2}`); + } catch (error) { + console.log("Operation failed"); + } + }); +} + +async function randomSealedSeed() { + console.log("Deploying contract"); + const contractFactory = await ethers.getContractFactory("PseudoRandom"); + const contract: PseudoRandom = await contractFactory.deploy(); + await contract.deployed(); + const signers = await ethers.getSigners(); + const signer = signers[0]; + console.log( + `Signing a message with the account of address ${signer.address}` + ); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.question("Enter a random seed to be signed:\n", async (seed) => { + const signedMessage = await signer.signMessage(seed); + rl.close(); + console.log(`The signed message is:\n${signedMessage}`); + const sig = ethers.utils.splitSignature(signedMessage); + console.log("Saving signature at contract"); + await contract.setSignature(sig.v, sig.r, sig.s); + try { + console.log("Trying to get a number with the original seed"); + const randomNumber = await contract.getCombinedRandomNumber(seed); + console.log(`Random number result:\n${randomNumber}`); + console.log("Trying to get a number without the original seed"); + const fakeSeed = "FAKE_SEED"; + const randomNumber2 = await contract.getCombinedRandomNumber(fakeSeed); + console.log(`Random number result:\n${randomNumber2}`); + } catch (error) { + console.log("Operation failed"); + } + }); +} \ No newline at end of file diff --git a/Week19/randomness/scripts/TestRandom.ts b/Week19/randomness/scripts/TestRandom.ts index 97851ccf..e413548f 100644 --- a/Week19/randomness/scripts/TestRandom.ts +++ b/Week19/randomness/scripts/TestRandom.ts @@ -21,16 +21,16 @@ async function main() { tossCoin(); break; case 3: - // signature(); + signature(); break; case 4: - // sealedSeed(); + sealedSeed(); break; case 5: - // randomSealedSeed(); + randomSealedSeed(); break; case 6: - // randao(); + randao(); break; default: console.log("Invalid"); diff --git a/Week19/randomness/scripts/TestRandomAgain.ts b/Week19/randomness/scripts/TestRandomAgain.ts new file mode 100644 index 00000000..6e95aeda --- /dev/null +++ b/Week19/randomness/scripts/TestRandomAgain.ts @@ -0,0 +1,36 @@ +async function randao() { + const contractFactory = await ethers.getContractFactory("Random"); + contractFactory.deploy().then(async (result) => { + result.deployed().then(async (contract: Random) => { + const currentBlock = await ethers.provider.getBlock("latest"); + const randomNumber = await contract.getRandomNumber(); + console.log( + `Block number: ${currentBlock.number}\nBlock difficulty: ${currentBlock.difficulty}\nRandom number from this block difficulty: ${randomNumber}` + ); + await ethers.provider.send("evm_mine", [currentBlock.timestamp + 1]); + const currentBlock2 = await ethers.provider.getBlock("latest"); + const randomNumber2 = await contract.getRandomNumber(); + console.log( + `Block number: ${currentBlock2.number}\nBlock difficulty: ${currentBlock2.difficulty}\nRandom number from this block difficulty: ${randomNumber2}` + ); + await ethers.provider.send("evm_mine", [currentBlock2.timestamp + 1]); + const currentBlock3 = await ethers.provider.getBlock("latest"); + const randomNumber3 = await contract.getRandomNumber(); + console.log( + `Block number: ${currentBlock3.number}\nBlock difficulty: ${currentBlock3.difficulty}\nRandom number from this block difficulty: ${randomNumber3}` + ); + await ethers.provider.send("evm_mine", [currentBlock3.timestamp + 1]); + const currentBlock4 = await ethers.provider.getBlock("latest"); + const randomNumber4 = await contract.getRandomNumber(); + console.log( + `Block number: ${currentBlock4.number}\nBlock difficulty: ${currentBlock4.difficulty}\nRandom number from this block difficulty: ${randomNumber4}` + ); + await ethers.provider.send("evm_mine", [currentBlock4.timestamp + 1]); + const currentBlock5 = await ethers.provider.getBlock("latest"); + const randomNumber5 = await contract.getRandomNumber(); + console.log( + `Block number: ${currentBlock5.number}\nBlock difficulty: ${currentBlock5.difficulty}\nRandom number from this block difficulty: ${randomNumber5}` + ); + }); + }); + } \ No newline at end of file diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 00000000..56c28ed9 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "win32-x64-108", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/node_modules/.yarn-state.yml b/node_modules/.yarn-state.yml new file mode 100644 index 00000000..7ee72942 --- /dev/null +++ b/node_modules/.yarn-state.yml @@ -0,0 +1,10 @@ +# Warning: This file is automatically generated. Removing it is fine, but will +# cause your node_modules installation to become invalidated. + +__metadata: + version: 1 + nmMode: classic + +"root-workspace-0b6124@workspace:.": + locations: + - "" diff --git a/package.json b/package.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/package.json @@ -0,0 +1 @@ +{} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..722dc367 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,11 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + +"root-workspace-0b6124@workspace:.": + version: 0.0.0-use.local + resolution: "root-workspace-0b6124@workspace:." + languageName: unknown + linkType: soft