From 61dfd0ab74f18e463caecbb4b30bf862624a4a10 Mon Sep 17 00:00:00 2001 From: Stefan Adolf Date: Mon, 10 Aug 2020 19:34:07 +0200 Subject: [PATCH 1/2] message signing and verification --- src/Main.tsx | 7 +++++++ src/SignForm.tsx | 37 +++++++++++++++++++++++++++++++++++++ src/VerifyForm.tsx | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 src/SignForm.tsx create mode 100644 src/VerifyForm.tsx diff --git a/src/Main.tsx b/src/Main.tsx index f979298..6eadcfb 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -5,6 +5,8 @@ import Web3 from "web3"; import ADIToken from "./contracts/ADIToken.json"; import TransferForm from "./TransferForm"; import _secrets from "../.secrets.json"; +import SignForm from "./SignForm"; +import VerifyForm from "./VerifyForm"; const Main: React.FC = () => { const { account, library: web3 } = useWeb3React(); @@ -53,9 +55,14 @@ const Main: React.FC = () => { ({_secrets.contractAddress})

to spare with others.

+ +

Transfer ADI

{adiBalance && ( )} +

Sign a message

+
+
); }; diff --git a/src/SignForm.tsx b/src/SignForm.tsx new file mode 100644 index 0000000..71639ac --- /dev/null +++ b/src/SignForm.tsx @@ -0,0 +1,37 @@ +import React, { useState } from "react"; +import { useWeb3React } from "@web3-react/core"; +import Web3 from "web3"; + +const SignForm = () => { + const { account, library: web3 } = useWeb3React(); + const [message, setMessage] = useState(""); + + const [signature, setSignature] = useState(""); + + async function signMessage() { + const signed = await web3.eth.personal.sign(message, account, "") + setSignature(signed); + } + + return ( +
+ + setMessage(e.target.value)} + /> + + +
+ {signature && + Signature: {signature} + } + +
+ ); +}; + +export default SignForm; diff --git a/src/VerifyForm.tsx b/src/VerifyForm.tsx new file mode 100644 index 0000000..d720d30 --- /dev/null +++ b/src/VerifyForm.tsx @@ -0,0 +1,44 @@ +import React, { useState } from "react"; +import { useWeb3React } from "@web3-react/core"; +import Web3 from "web3"; + +const VerifyForm = () => { + const { library: web3 } = useWeb3React(); + const [message, setMessage] = useState(""); + const [signature, setSignature] = useState(""); + const [pubAddress, setPubAddress] = useState(""); + + async function verifyMessage() { + const recovered = await web3.eth.personal.ecRecover(message, signature); + setPubAddress(recovered); + } + + return ( +
+ + setMessage(e.target.value)} + /> + + + setSignature(e.target.value)} + /> + + +
+ { pubAddress && + public address of the signer: {pubAddress} + } + +
+ ); +}; + +export default VerifyForm; From c574d4f789fbc2a5e85cb0af9f0442ca470c90e8 Mon Sep 17 00:00:00 2001 From: Stefan Adolf Date: Tue, 11 Aug 2020 23:27:06 +0200 Subject: [PATCH 2/2] verifiying locally & on chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deployed Verifier on Görli --- .openzeppelin/goerli.json | 25 +++++++++ .openzeppelin/project.json | 3 +- contracts/Verifier.json | 1 + contracts/Verifier.sol | 23 ++++++++ src/Main.tsx | 2 +- src/VerifyForm.tsx | 108 ++++++++++++++++++++++++++++-------- src/contracts/Verifier.json | 1 + 7 files changed, 137 insertions(+), 26 deletions(-) create mode 120000 contracts/Verifier.json create mode 100644 contracts/Verifier.sol create mode 120000 src/contracts/Verifier.json diff --git a/.openzeppelin/goerli.json b/.openzeppelin/goerli.json index a2b52f7..e94571e 100644 --- a/.openzeppelin/goerli.json +++ b/.openzeppelin/goerli.json @@ -288,6 +288,22 @@ ], "storageDiff": [] } + }, + "Verifier": { + "address": "0xB735f8730debFF357593dba17a7Fa3dD783b3DAe", + "constructorCode": "608060405234801561001057600080fd5b506105e7806100206000396000f3fe", + "bodyBytecodeHash": "cdcbeb651e8382f67e592daa834afb098964fcd557a81f930da622eb8eef0c64", + "localBytecodeHash": "52180514532af933f2e70d0473a69942db0b6637089152e6560d45ec6eeb3e58", + "deployedBytecodeHash": "52180514532af933f2e70d0473a69942db0b6637089152e6560d45ec6eeb3e58", + "types": {}, + "storage": [], + "warnings": { + "hasConstructor": false, + "hasSelfDestruct": false, + "hasDelegateCall": false, + "hasInitialValuesInDeclarations": false, + "uninitializedBaseContracts": [] + } } }, "solidityLibs": {}, @@ -307,6 +323,15 @@ "admin": "0x5c19BF66CC2c9386Cdf06d346F32F140A4559be9", "kind": "Upgradeable" } + ], + "ledger-academy/Verifier": [ + { + "address": "0xB0e0f956693eC1e990EB95C3a435B80548F7db1b", + "version": "1.0.0", + "implementation": "0xB735f8730debFF357593dba17a7Fa3dD783b3DAe", + "admin": "0x5c19BF66CC2c9386Cdf06d346F32F140A4559be9", + "kind": "Upgradeable" + } ] }, "manifestVersion": "2.2", diff --git a/.openzeppelin/project.json b/.openzeppelin/project.json index dde69e6..0c7e390 100644 --- a/.openzeppelin/project.json +++ b/.openzeppelin/project.json @@ -1,7 +1,8 @@ { "manifestVersion": "2.2", "contracts": { - "ADIToken": "ADIToken" + "ADIToken": "ADIToken", + "Verifier": "Verifier" }, "dependencies": { "@openzeppelin/contracts-ethereum-package": "^3.0.0" diff --git a/contracts/Verifier.json b/contracts/Verifier.json new file mode 120000 index 0000000..741b253 --- /dev/null +++ b/contracts/Verifier.json @@ -0,0 +1 @@ +../build/contracts/Verifier.json \ No newline at end of file diff --git a/contracts/Verifier.sol b/contracts/Verifier.sol new file mode 100644 index 0000000..7f6e51f --- /dev/null +++ b/contracts/Verifier.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.6.0; + +import "@openzeppelin/contracts-ethereum-package/contracts/cryptography/ECDSA.sol"; + +// https://www.codementor.io/@yosriady/signing-and-verifying-ethereum-signatures-vhe8ro3h6 +// -> part of oz: https://docs.openzeppelin.com/contracts/3.x/api/cryptography#ECDSA-recover-bytes32-bytes- + +contract Verifier { + function recoverAddr( + bytes32 msgHash, + bytes memory signature + ) public pure returns (address) { + return ECDSA.recover(msgHash, signature); + } + + function recoverAddrFromNonEthHash( + bytes32 msgHash, + bytes memory signature + ) public pure returns (address) { + bytes32 ethHash = ECDSA.toEthSignedMessageHash(msgHash); + return recoverAddr(ethHash, signature); + } +} diff --git a/src/Main.tsx b/src/Main.tsx index 6eadcfb..f9a60dd 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -38,7 +38,6 @@ const Main: React.FC = () => { const readableAdiBalance = Web3.utils.fromWei(_adiBalance); setADIBalance(readableAdiBalance); } - })(); }, [web3]); @@ -62,6 +61,7 @@ const Main: React.FC = () => { )}

Sign a message


+

Verify a message


); diff --git a/src/VerifyForm.tsx b/src/VerifyForm.tsx index d720d30..a35e507 100644 --- a/src/VerifyForm.tsx +++ b/src/VerifyForm.tsx @@ -1,41 +1,101 @@ import React, { useState } from "react"; import { useWeb3React } from "@web3-react/core"; import Web3 from "web3"; +import VerifierABI from "./contracts/Verifier.json"; + +import _secrets from "../.secrets.json"; const VerifyForm = () => { - const { library: web3 } = useWeb3React(); + const { library: web3, account } = useWeb3React(); const [message, setMessage] = useState(""); const [signature, setSignature] = useState(""); - const [pubAddress, setPubAddress] = useState(""); + const [localVerificationResult, setLocalVerificationResult] = useState(""); + const [contractVerificationResult, setContractVerificationResult] = useState(""); + const [nonEthSignatureVerificationResult, setNonEthSignatureVerificationResult] = useState(""); + + const contract = new web3.eth.Contract( + VerifierABI.abi, + _secrets.verifierContractAddress + ); + + // this will only work with messages that have been a hash by itself ;) + async function verifyNonEthSignatureOnContract() { + const nonEthMessageHash = web3.utils.sha3(message); + const recovered = await contract.methods.recoverAddrFromNonEthHash( + nonEthMessageHash, + signature + ).call() - async function verifyMessage() { + console.log(recovered); + setNonEthSignatureVerificationResult(recovered); + } + + async function verifySignatureOnContract() { + const verifiableMessage = `\x19Ethereum Signed Message:\n${message.length}${message}`; + const verifiableMessageSha = web3.utils.sha3(verifiableMessage); + + const recovered = await contract.methods.recoverAddr( + verifiableMessageSha, + signature + ).call() + + console.log(recovered); + setContractVerificationResult(recovered); + } + + async function verifySignatureLocally() { const recovered = await web3.eth.personal.ecRecover(message, signature); - setPubAddress(recovered); + console.log(recovered); + setLocalVerificationResult(web3.utils.toChecksumAddress(recovered)); + } + + async function verifySignature() { + verifySignatureLocally(); + verifySignatureOnContract(); + verifyNonEthSignatureOnContract(); } + function eqAccount(addr: string) { + return addr === account + } return (
- - setMessage(e.target.value)} - /> - - - setSignature(e.target.value)} - /> - - + + setMessage(e.target.value)} + /> + + + setSignature(e.target.value)} + /> + + + +
+ + { localVerificationResult && + + local verification: {localVerificationResult}
- { pubAddress && - public address of the signer: {pubAddress} - } +
+ } + { contractVerificationResult && + + contract verification: {contractVerificationResult}
+
+ } + { nonEthSignatureVerificationResult && + + non eth signed verification: {nonEthSignatureVerificationResult} + + }
); diff --git a/src/contracts/Verifier.json b/src/contracts/Verifier.json new file mode 120000 index 0000000..a2b024c --- /dev/null +++ b/src/contracts/Verifier.json @@ -0,0 +1 @@ +../../build/contracts/Verifier.json \ No newline at end of file