首页 > 其他分享 >用于数字签名与验签的dApp

用于数字签名与验签的dApp

时间:2024-07-05 10:19:18浏览次数:11  
标签:与验 name 数字签名 bytes32 dApp getElementById const document type

只有前端与链上合约两个组成部分的小dApp,其中前端使用ethers.js与Metamask钱包进行交互、以及提供hash和签名功能;链端是一个Solidity合约,提供验签功能。

页面示意

前端

用ChatGPT辅助生成的代码
app.html

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Verifying Signature on Ethereum</title>
    <link rel="stylesheet" href="styles.css">
    <script src="app.js"  type="module"></script>
</head>
<body>
    <div class="container">
        <h1>Verifying Signature on Ethereum</h1>
        <button id="connectButton" class="button">Connect Wallet</button>

        <div id="signPage" class="card">
            <h2>Sign Message</h2>
            <input type="text" id="to" placeholder="To Address" class="input">
            <input type="number" id="amount" placeholder="Amount" class="input">
            <input type="text" id="message" placeholder="Message" class="input">
            <input type="number" id="nonce" placeholder="Nonce" class="input">
            <button id="signButton" class="button">Sign Message</button>
            <p id="signature" class="output"></p>
            <button id="goToVerify" class="button">Go to Verify</button>
        </div>

        <div id="verifyPage" class="card hidden">
            <h2>Verify Signature</h2>
            <input type="text" id="verifyTo" placeholder="To Address" class="input">
            <input type="number" id="verifyAmount" placeholder="Amount" class="input">
            <input type="text" id="verifyMessage" placeholder="Message" class="input">
            <input type="number" id="verifyNonce" placeholder="Nonce" class="input">
            <input type="text" id="signatureInput" placeholder="Signature" class="input">
            <input type="text" id="claimedSigner" placeholder="Claimed Signer Address" class="input">
            <button id="verifyButton" class="button">Verify Signature</button>
            <p id="verificationResult" class="output"></p>
            <button id="goToSign" class="button">Go to Sign</button>
        </div>
    </div>
</body>
</html>

样式type.css

body {
    font-family: Arial, sans-serif;
    background: url('background.jpg') no-repeat center center fixed;
    background-size: cover;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
}

.container {
    background: rgba(255, 255, 255, 0.9);
    border-radius: 10px;
    padding: 20px;
    width: 300px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    text-align: center;
}

h1 {
    margin-bottom: 20px;
    color: #333;
}

.card {
    background: #f9f9f9;
    border-radius: 10px;
    padding: 15px;
    margin-bottom: 20px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}

.hidden {
    display: none;
}

.input {
    width: 100%;
    padding: 10px;
    margin: 10px 0;
    border: 1px solid #ccc;
    border-radius: 5px;
    box-sizing: border-box;
}

.button {
    background-color: #007bff;
    color: white;
    padding: 10px 15px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 16px;
}

.button:hover {
    background-color: #0056b3;
}

.output {
    word-break: break-all;
    background: #e9ecef;
    padding: 10px;
    border-radius: 5px;
    margin-top: 10px;
    font-size: 14px;
    color: #333;
}

app.js

import { ethers } from "https://cdn.jsdelivr.net/npm/ethers/dist/ethers.esm.min.js";
let signer;
let contract;

const connectButton = document.getElementById('connectButton');
const signButton = document.getElementById('signButton');
const verifyButton = document.getElementById('verifyButton');
const goToVerifyButton = document.getElementById('goToVerify');
const goToSignButton = document.getElementById('goToSign');

connectButton.onclick = connectWallet;
signButton.onclick = signMessage;
verifyButton.onclick = verifySignature;
goToVerifyButton.onclick = showVerifyPage;
goToSignButton.onclick = showSignPage;

const abi = [
                {
                    "inputs": [
                        {
                            "internalType": "address",
                            "name": "to",
                            "type": "address"
                        },
                        {
                            "internalType": "uint256",
                            "name": "amount",
                            "type": "uint256"
                        },
                        {
                            "internalType": "string",
                            "name": "message",
                            "type": "string"
                        },
                        {
                            "internalType": "uint256",
                            "name": "nonce",
                            "type": "uint256"
                        }
                    ],
                    "name": "getMessageHash",
                    "outputs": [
                        {
                            "internalType": "bytes32",
                            "name": "",
                            "type": "bytes32"
                        }
                    ],
                    "stateMutability": "pure",
                    "type": "function"
                },
                {
                    "inputs": [
                        {
                            "internalType": "bytes32",
                            "name": "messageHash",
                            "type": "bytes32"
                        }
                    ],
                    "name": "getEthSignedMessageHash",
                    "outputs": [
                        {
                            "internalType": "bytes32",
                            "name": "",
                            "type": "bytes32"
                        }
                    ],
                    "stateMutability": "pure",
                    "type": "function"
                },
                {
                    "inputs": [
                        {
                            "internalType": "address",
                            "name": "signer",
                            "type": "address"
                        },
                        {
                            "internalType": "address",
                            "name": "to",
                            "type": "address"
                        },
                        {
                            "internalType": "uint256",
                            "name": "amount",
                            "type": "uint256"
                        },
                        {
                            "internalType": "string",
                            "name": "message",
                            "type": "string"
                        },
                        {
                            "internalType": "uint256",
                            "name": "nonce",
                            "type": "uint256"
                        },
                        {
                            "internalType": "bytes",
                            "name": "signature",
                            "type": "bytes"
                        }
                    ],
                    "name": "verify",
                    "outputs": [
                        {
                            "internalType": "bool",
                            "name": "",
                            "type": "bool"
                        }
                    ],
                    "stateMutability": "pure",
                    "type": "function"
                }
            ];

async function connectWallet() {
    if (typeof window.ethereum !== 'undefined') {
        try {
            const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
            const provider = new ethers.providers.Web3Provider(window.ethereum);
            signer = provider.getSigner();
            console.log('Wallet connected: ', accounts[0]);
            alert('Wallet connected: ' + accounts[0]);

            const contractAddress = "0x262D7b573e1c1e964A8FbcD1375Acfd97168Ac5F";
            
            contract = new ethers.Contract(contractAddress, abi, signer);
        } catch (error) {
            console.error('Error connecting to wallet: ', error);
        }
    } else {
        alert('MetaMask is not installed!');
    }
}

async function signMessage() {
    const to = document.getElementById('to').value;
    const amount = document.getElementById('amount').value;
    const message = document.getElementById('message').value;
    const nonce = document.getElementById('nonce').value;

    const messageHash = ethers.utils.solidityKeccak256(['address', 'uint256', 'string', 'uint256'], [to, amount, message, nonce]);
    const signature = await signer.signMessage(ethers.utils.arrayify(messageHash));
    document.getElementById('signature').innerText = 'Signature: ' + signature;
    console.log('Signature: ', signature);
}

async function verifySignature() {
    const to = document.getElementById('verifyTo').value;
    const amount = document.getElementById('verifyAmount').value;
    const message = document.getElementById('verifyMessage').value;
    const nonce = document.getElementById('verifyNonce').value;
    const signature = document.getElementById('signatureInput').value;
    const claimedSigner = document.getElementById('claimedSigner').value;

    const isValid = await contract.verify(claimedSigner, to, amount, message, nonce, signature);
    document.getElementById('verificationResult').innerText = 'Signature is valid: ' + isValid;
    console.log('Signature is valid: ', isValid);
}

function showVerifyPage() {
    document.getElementById('signPage').classList.add('hidden');
    document.getElementById('verifyPage').classList.remove('hidden');
}

function showSignPage() {
    document.getElementById('verifyPage').classList.add('hidden');
    document.getElementById('signPage').classList.remove('hidden');
}

链端Solidity合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;


import "hardhat/console.sol";

contract VerifySignature {
 
    function getMessageHash(
        address _to,
        uint256 _amount,
        string memory _message,
        uint256 _nonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_to, _amount, _message, _nonce));
    }

   
    function getEthSignedMessageHash(bytes32 _messageHash)
        public
        pure
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash)
        );
    }

    /* 4. 验证签名  Verify signature
    signer = 0xB273216C05A8c0D4F0a4Dd0d7Bae1D2EfFE636dd
    to = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C
    amount = 123
    message = "coffee and donuts"
    nonce = 1
    signature =
        0x993dab3dd91f5c6dc28e17439be475478f5635c92a56e17e82349d3fb2f166196f466c0b4e0c146f285204f0dcb13e5ae67bc33f4b888ec32dfe0a063e8f3f781b
    */
    function verify(
        address _signer,
        address _to,
        uint256 _amount,
        string memory _message,
        uint256 _nonce,
        bytes memory signature
    ) public pure returns (bool) {
        bytes32 messageHash = getMessageHash(_to, _amount, _message, _nonce);
        bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
        console.log("result: %s",  (recoverSigner(ethSignedMessageHash, signature) == _signer) );
        return recoverSigner(ethSignedMessageHash, signature) == _signer;
    }
    
    //从签名和摘要中恢复签名者
    function recoverSigner(
        bytes32 _ethSignedMessageHash,
        bytes memory _signature
    ) public pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);

        return ecrecover(_ethSignedMessageHash, v, r, s);
    }

    function splitSignature(bytes memory sig)
        internal  
        pure 
        returns (bytes32 r, bytes32 s, uint8 v)
    {
        require(sig.length == 65, "invalid signature length");

        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

        // implicitly return (r, s, v)
    }
}

合约部署和测试脚本: 用的hardhat + ganache

const { ethers } = require("hardhat");
const { boolean } = require("hardhat/internal/core/params/argumentTypes");

async function main () {
    const [deployer] = await ethers.getSigners();
    //const accountBalance = await ethers.provider.getBalance(deployer.address);

    console.log("使用如下账户部署合约:", await deployer.getAddress());
    //console.log("账户余额:", accountBalance);

    const factory = await ethers.getContractFactory("VerifySignature");
    const contract = await factory.deploy();

    console.log("合约地址: ", await contract.getAddress());


    console.log("-----------------");
    const contractSigned = contract.connect(deployer);
    let bool = await contractSigned.verify("0x4953bc5fc7d5abd0cd86a198a4a34eb2e3b8dc1d",
                                            "0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C",
                                            123,
                                            "coffee and donuts",
                                            1,
                                            "0x823f00ad2f7e992f42284c9903fc511ab729b3f771f04622e0fede0f6c478d51456249f905bfafd42b3c340d5326820d79dff2dde570b585b0cc1ada32bc04531b"
                                        );
    console.log("验签结果:", bool);
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });

标签:与验,name,数字签名,bytes32,dApp,getElementById,const,document,type
From: https://www.cnblogs.com/lyhero11/p/18285240

相关文章

  • 主键Id自增,如何获取Id(Dapper)
    这里用的是Dapper,以前用EF的时候好像有用到过db.savechanges().但是项目中没有这个,所以用以下的方法去获取id背景:涉及到多表入库,需要获取主表的Id,所以用到了这个(timeFields 可以忽略)  ///<summary>///单个添加///</summary>///<typ......
  • DApp设计与开发 课程笔记(六):NFT交易市场后端开发
    笔记对应课程内容为成都信息工程大学区块链产业学院老师梁培利的DApp设计与开发17-18课笔记中提到的名词不做过多解释不懂就搜!tokenuri对应一个metadata的json数据上传一个图片,将图片上传到IPFS,获得一个cid,然后将json格式的metadata上传到IPFS,然后给用户发送一个NFT......
  • DApp设计与开发 课程笔记(四):NFT交易市场合约
    笔记对应课程内容为成都信息工程大学区块链产业学院老师梁培利的DApp设计与开发10-13课笔记中提到的名词不做过多解释不懂就搜!开发准备:编辑IDE环境:Vscode+Hardhat调试:RemixERC标准智能合约生成巫师:https://docs.openzeppelin.com/contracts/5.x/wizard动手学Windo......
  • SpringBoot整合JWT(JSON Web Token)生成token与验证
    目录JWT什么是JWTJWT使用流程确定要传递的信息:生成JWT:JWT传输:客户端保存JWT:客户端发送JWT:服务器验证JWT:服务器响应:Token的使用示例:工具类R结果集返回一个生成的token创建拦截器JWT什么是JWTJWT(JSONWebToken)是是目前最流行的跨域认证解决方案。它通常被......
  • 震惊:全面拆解dapp上线三天、 规则漏洞导致资金全部损失
    背景:今天的分析的是链上一个土狗项目由于规则设计漏洞、导致被黑客利用漏洞攻击、致使资金全部损失的案例、近期这个项目也是圈内很火、今天看到了,就趁周末有时间从技术的角度,分析黑客如何利用链上部署的合约,进而干废项目方。今天说的这个,不是技术漏洞bug、是规则设计漏洞......
  • 等精度频率计的设计与验证
    文章摘要:借助于QuartusIIPLL_IP核产生一个任意频率被测时钟信号,设计一个等精度测量模块,通过其处理后,再数码管上显示出六位的测量频率数值,验证测量的准确度。关键词:VerilogHDL;等精度频率测量;数码管;PLL_IP核最终框图:频率计,即频率计数器,专用于测量被测信号频率,基本工作原理就......
  • 针对PDF文档:印章、数字签名、编辑保护、PDF/A的Java工具类
    背景  本文是基于Java语言,引入POI从而提供将富文本编辑器内的html内容转换为docx的方式。代码  引入pom坐标<dependency><groupId>cn.net.pap</groupId><artifactId>pap4j-common-pdf</artifactId><version>0.0.1</version></......
  • DApp应用开发概述
    随着区块链技术的迅猛发展,去中心化应用(DecentralizedApplications,简称DApps)逐渐成为技术前沿的热点。DApps与传统的中心化应用有着本质的区别,它们运行在去中心化的区块链网络上,无需依赖中心化服务器,具有高透明度、高安全性和不可篡改性等特点。基于基纳链开发的dapp实例区块......
  • 探索Solana链上DApp开发:高性能区块链生态的新机遇
    Solana是一个新兴的区块链平台,致力于为DApp(去中心化应用程序)开发者提供高性能、低成本的解决方案。Solana的独特之处在于其创新性的共识机制和高吞吐量的网络,使得开发者可以构建高度可扩展的DApp,并为用户提供无与伦比的体验。以下是一份简要介绍,让您可以快速了解Solana链......
  • Dapper升级SqlSugar问题汇总
    最近群里有个小伙伴把Dapper迁移SqlSugar几个不能解决的问题进行一个汇总,我正好写一篇文章来讲解一下 一、sqlwherein传参问题:SELECT*FROMuserswhereidIN@ids答:SqlSugar中应该是//SELECT*FROMuserswhereidIN(@ids)varlistdb.Ado.SqlQuery<Users......