Conflux智能合约学习_FluentWallet进行签名使用Contract进行签名验证

一、智能合约Hash代码

语法:

keccak256(abi.encodePacked(text1,text));

返回byte32类型的hash值

代码:

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

contract HashContract {

    function hashAbiEncode(string memory text1,string memory text) external pure returns(bytes32){
        return keccak256(abi.encodePacked(text1,text));
    }

    function hashAbiEncodePacked(string memory text1,string memory text2) external pure returns (bytes32) {
        return keccak256(abi.encode(text1,text2));
    }


    function hashAbiEncodePackedNum(string memory text1,uint num,string memory text2) external pure returns (bytes32) {
        return keccak256(abi.encodePacked(text1,num,text2));
    }

    // @dev calc hash value
    function hash(string memory text,uint num,string memory text1) external pure returns(bytes32){
        return keccak256(abi.encodePacked(text,num,text1));
    }

    function encodeTest(string memory text,string memory text1) external pure returns(bytes memory){
       return abi.encode(text,text1); 
    }

    function encodePackedTest(string memory text,string memory text1) external pure returns(bytes memory) {
        return abi.encodePacked(text,text1);
    }

}

说明:

1.abi.encode(param1,param2) 和 abi.encodePacked(param1,param2) 的区别

abi.encode(“AAA”,“BB”): 返回的结果为:

 0x4141414242

abi.encodePacked(“AAA”,“BB”): 返回结果为:

0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003414141000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024242000000000000000000000000000000000000000000000000000000000000

encodePacked会填充0,这样abi.encodePacked(“AAA”,“BB”) 和 abi.encodePacked(“AA”,“ABB”)计算出来的hash值是不一样的

2.使用abi.encode(param1,param2) 会存在hash碰撞的可能

比如abi.encode(“AAA”,“BB”) 的返回结果为:

 0x4141414242

abi.encode(“AA”,“ABB”) 的返回结果为:

0x4141414242

貌似进行预算的时候是直接组合两个参数的字符串,这样就导致这两次计算的都相当于同一个字符串"AAABB"

二、通过Hash验证签名

可以使用的场景:

验证签名的过程:

下面有合约代码,我将合约代码部署到Conflux Test网络上进行上述流程的测试

ConfluxScan Test 测试网上合约地址:

cfxtest:accu714t11k574edkeh8txnr63ubfb2dtpsj2gvmgf

1.首先使用Fluent Wallet钱包对需要签名的字符串进行签名

待签名的字符串:

我祝愿浏览该帖子的各位,一夜暴富!!!功成名就。

打开Chrome F12的控制台输入

account = "cfxtest:aam81t80tu6f5zamuk0hycy14urec6xc8aepw5cmt2"
hashStr = "我祝愿浏览该帖子的各位,一夜暴富!!!功成名就。"
conflux.request({method: "personal_sign", params: [hashStr,account],from :account})

account : 要签名的地址

hashStr: 要签名的内容

签名过后生成的字符串:

0x23d1df64cba9773cfedb798344bd52ccd77c979fab6a52f28748a52b855b1cad689801d7aaffeffbb00f9291dec68e54e80724b8855f4ee30022e114bb715f0a00

之后就可以上部署的合约上进行验证了

代码实现:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract SignVaild{

    // return string hash value
    function getMessageHash(string memory _message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_message));
    }
    

   // int convert to string.    
   function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len;
        while (_i != 0) {
            k = k-1;
            uint8 temp = (48 + uint8(_i - _i / 10 * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            _i /= 10;
        }
        return string(bstr);
    }

    // return string length that type is uint256
    function strLen(string memory s) public pure returns ( uint256) {
        return bytes(s).length;
    }

    // join string.
    function append(string memory a, string memory b) internal pure returns (string memory) {
        return string(abi.encodePacked(a, b));
    }

    // 测试拼接字符串
    function testConcat(string memory _message) public pure returns (string memory){
        string memory messageLen = uint2str(strLen(_message));
        return append("\x19Conflux Signed Message:\n",messageLen);
    }

    // 将待检查的文本内容转换成hash
    function getCfxSignedMessageHash(string memory _message) public pure returns (bytes32){
        string memory messageLen = uint2str(strLen(_message));
        return keccak256(abi.encodePacked(
            append("\x19Conflux Signed Message:\n",messageLen),
            _message
        ));
    }


    // 传递 待检验的字符串hash值和 Fluent钱包签名生成的字符串
    function recover(bytes32 _cfxSignedMessageHash,bytes memory _sig) public pure returns (address){

        (bytes32 r, bytes32 s, uint8 v) = _split(_sig);
        return ecrecover(_cfxSignedMessageHash,v,r,s);

    }

    // 通过签名字符串提取出 r,s,v
    function _split(bytes memory _sig) public pure returns (bytes32 r,bytes32 s, uint8 v){
        require(_sig.length ==65 , "invalid sign");
        assembly {
            r := mload(add(_sig,32))
            s := mload(add(_sig,64))
            v := byte(0,mload(add(_sig,96)))
        }
        return (r,s,v);
    }

    // 校验签名该签名是否由 指定字符串和指定地址签名出来的 
    function verfiy(address _signer,string memory _message,bytes memory _sig) external pure returns(bool){
        
        // 传递待检验的字符串 生成hash字符串
        bytes32 cfxSignerMessageHash = getCfxSignedMessageHash(_message);

        return recover(cfxSignerMessageHash,_sig) == _signer;

    }


}

Java 代码验证:

这个是使用 brander 大佬的代码,多亏大佬的代码让我断点调试然后对比智能合约计算出来的结果,才找到合约中的代码问题

如果有幸让大佬看见了,不懂就问 \u0019 这个有什么用?

import conflux.web3j.types.AddressType;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.utils.Numeric;

import java.math.BigInteger;
import java.security.SignatureException;
import java.util.Arrays;

public class ConfluxFluentDemo {

    public static String recoverFluentAddressFromSignature(String signature, String message) throws SignatureException {
        String PERSONAL_MESSAGE_PREFIX = "\u0019Conflux Signed Message:\n";
        String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
        byte[] msgHash = (prefix + message).getBytes();

        byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
        byte v = signatureBytes[64];
        if (v < 27) {
            v += 27;
        }

        Sign.SignatureData sd = new Sign.SignatureData(
                v,
                (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32),
                (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));

        boolean match = false;
        BigInteger pubkey = Sign.signedMessageToKey(msgHash, sd);
        String addressRecovered = "0x" + Keys.getAddress(pubkey);
        System.out.println("-22-->" + addressRecovered);
        String hexAddress = AddressType.User.normalize(addressRecovered);
        System.out.println("--> " + hexAddress);
        return hexAddress;
    }

    public static void main(String[] args) {
        try {
            System.out.println(recoverFluentAddressFromSignature("0x0e730f15d5df13b6dedccc2f2df5c4924d2efde4f5f76908d35b79a7407bb12206b1d6e2ff0e9c0205fc74b327de840d878604b732506a085ccf963dfb0f4b4600",
                    "secret message"));
        } catch (SignatureException e) {
            e.printStackTrace();
        }
    }
}

Maven POM 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.plumblossom.javase</groupId>
    <artifactId>java-knowledge</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>java-collection</module>
    </modules>

    <dependencies>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

        <dependency>
            <groupId>org.web3j</groupId>
            <artifactId>core</artifactId>
            <version>4.3.0</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>

参考大佬的签名验证地址: JAVA 版本 personal_sign 签名验证2 (personal_sign portal与fluent的不同点)

3 Likes