知世金融网专注于股票行情,期货开户,外汇储备等最新相关资讯信息提供投资者参考学习!

当前位置:网站首页 > 区块链 > 正文

Qtum量子链发布QIP-6,通过预编译合约大幅降低开发成本

原创
文章作者
知世-金融领域资深作者
知名金融领域作者,从事金融超过十余年,在行业内有一定影响力。
金融风险管理师认证证书 常识职业资格认证 特许金融分析师 国际金融理财师认证证书
发布时间:2020-02-16 18:54:11 发布来源:Qtum量子链 文章点击:92

背 景 以太坊中的ecrecover函数可以用来获取对一条消息签名的地址。这对于证明一条消息或者一段数据被一个指定的账户签名过(而不是被篡改过)非常有用。但是 Qtum 没有使用以太坊的...

目录

    本文标题Qtum量子链发布QIP-6,通过预编译合约大幅降低开发成本,作者:知世,本文有3774个文字,大小约为13KB,预计阅读时间10分钟,请您欣赏。知世金融网众多优秀文章,如果想要浏览更多相关文章,请使用网站导航的搜索进行搜索。本站虽然不乏优秀之作,但仅作为投资者学习参考。

    背 景

    以太坊中的ecrecover函数可以用来获取对一条消息签名的地址。这对于证明一条消息或者一段数据被一个指定的账户签名过(而不是被篡改过)非常有用。但是 Qtum 没有使用以太坊的账户模型,而是采用比特币的 UTXO 模型,地址的算法也和以太坊不同,因此这个函数并不适用于 Qtum。在一些需要验证签名来源信息的情况下, Qtum 开发者并不能方便的在智能合约中完成这个验证,而是需要在合约中完整实现或者调用一次从签名和消息获取签名者公钥的合约,会造成非常大的开销,进而使得相应合约的调用费用非常高。

    问题的细节

    ecrecover接受一个消息的哈希和消息的签名,然后计算出签名的私钥对应的公钥,并将该公钥转换为以太坊地址格式。然而以太坊的地址算法和 Qtum 不同,而且ecrecover返回的是公钥经过哈希以后的结果,这个过程不可逆,因此在 Qtum 上无法使用这个函数。

    在以太坊中,地址计算方法如下:

    keccak256(pubkey)

    而在 Qtum 上,地址的计算方式和比特币相同,使用如下计算方法:

    ripemd160(sha256(pubkey))

    在 Qtum 的合约中,msg.sender是一个 Qtum 地址。由于从公钥开始转换为地址的每一步操作都是不可逆的,ecrecover返回的以太坊地址无法和msg.sender中的 Qtum 地址进行比较。而现有的 Qtum 智能合约中并没有提供任何函数来从消息签名中获取 Qtum 地址,这导致 Qtum 智能合约开发者们不得不开发或使用Secp256k1相关的库来计算签名公钥和地址,造成更大的计算开销和更高的合约费用。

    另一个需要注意的细节是,Qtum 沿用的比特币消息签名算法和以太坊的消息签名算法的实现上有一些细微的差别:

    以太坊的签名按如下格式组成:

    [r][s][v]

    而 Qtum 的签名则是:

    [v][r][s]

    其中v是 recover id,r是椭圆曲线上的一个点R的X坐标,s是这个点R的Y坐标。如上的不同导致 Qtum 和以太坊的 recover 算法的实现细节也不相同。

    QIP-6 的解决方案

    通过在 Qtum 的虚拟机中增加一个预编译的合约,以提供一个用来调用 Qtum 核心代码中的 recover 代码的接口。智能合约开发者只需要写简单的一两个函数就能从签名消息中获取到签名者的地址。新增的预编译合约的接口和ecrecover保持一致。

    什么是预编译合约

    预编译合约是 EVM 中为了提供一些不适合写成 opcode 的较为复杂的库函数(多数用于加密、哈希等复杂计算)而采用的一种折中方案。由于它是用底层代码实现的,执行速度快,对于开发者来说就比直接用运行在 EVM 上的函数消耗更低。以太坊中使用预编译合约提供一些常用的较为繁琐的操作,比如sha256、ripemd160hash等。

    预编译合约的实现

    预编译合约的核心代码由虚拟机底层(C++)实现,通过在虚拟机的初始化过程中注册到人为指定的固定地址上来提供智能合约调用的接口。

    预编译合约的使用

    一个典型的调用方式:

    assembly {
    if iszero(call(gasLimit, contractAddress, value, input, inputLength, output, outputLength)) {
    revert(0, 0)
    }
    }

    在新版本的虚拟机中,还可以使用staticcall:

    assembly {
    success := staticcall(gasLimit, contractAddress, input, inputLength, output, outputLength)
    }

    其中contractAddress就是要调用的预编译合约的地址,本次 Qtum 新增的 btc_ecrecover 的地址是0x85。input是调用合约的参数列表。这个调用的返回值代表了调用是否成功,1表示成功,0表示失败。而返回的数据会写入到output里面。

    下面我们看一个例子:

    pragma solidity ^0.5.0;

    /**
    * @title Elliptic curve signature operations
    * @dev Based on

    https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
    * TODO Remove this library once solidity supports passing a signature to ecrecover.
    * See https://github.com/ethereum/solidity/issues/864
    */

    library ECDSA {

    /**
    * @dev Recover signer address from a message by using their signature.
    * @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address.
    * @param signature bytes signature, the signature is generated using web3.eth.sign()
    */
    function recover(bytes32 hash, bytes memory signature) internal view returns (address) {
    // Check the signature length
    if (signature.length != 65) {
    return (address(0));
    }

    // Divide the signature in r, s and v variables
    bytes32 r;
    bytes32 s;
    uint8 v;

    // ecrecover takes the signature parameters, and the only way to get them
    // currently is to use assembly.
    // solhint-disable-next-line no-inline-assembly
    assembly {
    v := byte(0, mload(add(signature, 0x20)))
    r := mload(add(signature, 0x21))
    s := mload(add(signature, 0x41))
    }

    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
    // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
    //
    // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
    // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
    // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
    // these malleable signatures as well.
    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
    return address(0);
    }

    // Support both compressed or uncompressed
    if (v != 27 && v != 28 && v != 31 && v != 32) {
    return address(0);
    }

    // If the signature is valid (and not malleable), return the signer address
    return btc_ecrecover(hash, v, r, s);
    }

    function btc_ecrecover(bytes32 msgh, uint8 v, bytes32 r, bytes32 s) public view returns(address) {
    uint256[4] memory input;
    input[0] = uint256(msgh);
    input[1] = v;
    input[2] = uint256(r);
    input[3] = uint256(s);
    uint256[1] memory retval;

    uint256 success;
    assembly {
    success := staticcall(not(0), 0x85, input, 0x80, retval, 32)
    }

    if (success != 1) {
    return address(0);
    }

    return address(retval[0]);
    }
    }

    在上面这个例子中,只要调用btc_ecrecover函数就能获取到消息签名者的地址。为了简化输入,例子中也封装了一个recover函数,使得开发者只要传入原始签名就能完成合约调用。

    下面我们不使用预编译合约,而是完全使用 solidity 代码实现 btc_ecrecover功能。代码实现如下:

    pragma solidity ^0.4.26;
    import {ECCMath} from "github.com/androlo/standard-contracts/contracts/src/crypto/ECCMath.sol";
    import {Secp256k1} from "github.com/androlo/standard-contracts/contracts/src/crypto/Secp256k1.sol";

    library ECDSA {
    uint256 constant p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f;
    uint256 constant n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
    uint256 constant gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798;
    uint256 constant gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8;

    function recover(bytes32 hash, bytes memory signature) internal view returns (address) {
    if (signature.length != 65) {
    return (address(0));
    }

    bytes32 r;
    bytes32 s;
    uint8 v;

    assembly {
    v := byte(0, mload(add(signature, 0x20)))
    r := mload(add(signature, 0x21))
    s := mload(add(signature, 0x41))
    }

    if (uint256(s) > n / 2) {
    return address(0);
    }

    if (v != 27 && v != 28 && v != 31 && v != 32) {
    return address(0);
    }

    return btc_ecrecover(hash, v, r, s);
    }

    function btc_ecrecover(bytes32 msgh, uint8 v, bytes32 r, bytes32 s) public view returns (address) {
    uint i = 0;

    uint256 rr = uint256(r);
    uint256 ss = uint256(s);
    bool isYOdd = ((v - 27) & 1) != 0;
    bool isSecondKey = ((v - 27) & 2) != 0;
    bool isCompressed = ((v - 27) & 4) != 0;
    if (rr >= p % n && isSecondKey) {
    return address(0);
    }

    uint256[3] memory P = _getPoint(uint256(msgh), rr, ss, isYOdd, isSecondKey);
    if (P[2] == 0) {
    return address(0);
    }
    ECCMath.toZ1(P, p);

    bytes memory publicKey;
    if (isCompressed) {
    publicKey = new bytes(33);
    publicKey[0] = byte(P[1] % 2 == 0 ? 2 : 3);
    for (i = 0; i < 32; ++i) {
    publicKey[32 - i] = byte((P[0] >> (8 * i)) & 0xff);
    }
    } else {
    publicKey = new bytes(65);
    publicKey[0] = 4;
    for (i = 0; i < 32; ++i) {
    publicKey[32 - i] = byte((P[0] >> (8 * i)) & 0xff);
    publicKey[64 - i] = byte((P[1] >> (8 * i)) & 0xff);
    }
    }
    return address(ripemd160(sha256(publicKey)));
    }

    function _getPoint(uint256 msgh, uint256 r, uint256 s, bool isYOdd, bool isSecondKey) internal view returns (uint256[3] memory) {
    uint256 rx = isSecondKey ? r + n : r;
    uint256 ry = ECCMath.expmod(ECCMath.expmod(rx, 3, p) + 7, p / 4 + 1, p);
    if (isYOdd != (ry % 2 == 1)) {
    ry = p - ry;
    }

    uint256 invR = ECCMath.invmod(r, n);

    return Secp256k1._add(
    Secp256k1._mul(n - mulmod(msgh, invR, n), [gx, gy]),
    Secp256k1._mul(mulmod(s, invR, n), [rx, ry])
    );
    }
    }

    我们在测试链上部署了上述两个两个合约,地址分别如下:

    预编译合约: 21ea1d8376d1820d7091084a76f380143b59aaf8

    solidity实现: 4fdff1b4bde5edf13360ff0946518a01115ce818

    使用地址

    qQqip6i2e2buCZZNdqMw4VNpaYpnLm4JAx对消息btc_ecrecover test进行签名,我们得到btc_ecrecover 的调用参数:

    bytes32 msgh = 0xdfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d
    uint8 v = 0x20
    bytes32 r = 0xca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b753
    bytes32 s = 0x0731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

    两个合约调用调用结果如下:

    1.预编译合约

    callcontract 21ea1d8376d1820d7091084a76f380143b59aaf8 69bc0963dfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d0000000000000000000000000000000000000000000000000000000000000020ca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b7530731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

    {
    "address": "21ea1d8376d1820d7091084a76f380143b59aaf8",
    "executionResult": {
    "gasUsed": 32688,
    "excepted": "None",
    "newAddress": "21ea1d8376d1820d7091084a76f380143b59aaf8",
    "output": "0000000000000000000000004fdff1b4bde5edf13360ff0946518a01115ce818",
    "codeDeposit": 0,
    "gasRefunded": 0,
    "depositSize": 0,
    "gasForDeposit": 0,
    "exceptedMessage": ""
    },
    "transactionReceipt": {
    "stateRoot": "5d9e1ad1b5d09e9e7c41d09078434488927366adf8ebf5a0049bb99610a817f1",
    "gasUsed": 32688,
    "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "log": []
    }
    }

    2.solidity 实现

    callcontract d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14 69bc0963dfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d0000000000000000000000000000000000000000000000000000000000000020ca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b7530731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

    {
    "address": "d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14",
    "executionResult": {
    "gasUsed": 886077,
    "excepted": "None",
    "newAddress": "d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14",
    "output": "0000000000000000000000004fdff1b4bde5edf13360ff0946518a01115ce818",
    "codeDeposit": 0,
    "gasRefunded": 0,
    "depositSize": 0,
    "gasForDeposit": 0,
    "exceptedMessage": ""
    },
    "transactionReceipt": {
    "stateRoot": "5d9e1ad1b5d09e9e7c41d09078434488927366adf8ebf5a0049bb99610a817f1",
    "gasUsed": 886077,
    "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "log": []
    }
    }

    可见预编译合约调用btc_ecrecover需要花费 32688 gas,而完全用 solidity 实现需要 886077 gas。预编译合约实现的 gas 花费远远少于 solidity 实现。

    QIP-6 的影响

    QIP-6 大大减小了智能合约开发者的开发成本。从调用合约的角度来说,如果完全用 solidity 在合约中实现 recover,其 gas 使用量远远超过btc_ecrecover函数。于是使用btc_ecrecover来获取消息签名地址的合约调用成本也大大降低。此外,QIP-6 也让 Qtum 的智能合约系统更加完备。

    另一方面,QIP-6 没有对原有的ecrecover进行修改,保持了 Qtum 和以太坊的兼容性,理论上不会带来任何风险。

    本文相关推荐: 如何通过预编译智能合约实现隐私协议,保护链上资产隐私?

    以上便是知世金融网给大家分享的关于Qtum量子链发布QIP-6,通过预编译合约大幅降低开发成本/qkl/27533.html的相关信息了,希望能帮助到大家,更多金融相关信息,敬请关注知世金融网!

    网站内容均来自互联网,如侵害您的利益联系客服进行删除!

    关键词:预编
    (0)
    (0)

    上一篇:上周5个企业区块链故事

    下一篇:开启新数字经济:区块链的互联网与数字资产的演进

    本文标题:Qtum量子链发布QIP-6,通过预编译合约大幅降低开发成本

    本文地址:/index.php?s=article&c=search&keyword=%E9%A2%84%E7%BC%96

    金融知名领域

    南方财富网 | 金融界 | 金融界 |

    更多推荐

    • 茅台吃饱,经销商哭倒
      茅台吃饱,经销商哭倒
    • 汇金的五次增持从短期看具有一定的“稳定器“作用,但从市场表现看效果逐次递减
      汇金的五次增持从短期看具有一定的“稳定器“作用,但从市场表现看效果逐次递减
    • 158亿元!比亚迪收购!
      158亿元!比亚迪收购!
    • 9月价格回落近五成 “冷静期”酒店业备战“十一”市场
      9月价格回落近五成 “冷静期”酒店业备战“十一”市场
    • 2023哈马博览会哈尔滨银行展区精彩纷呈
      2023哈马博览会哈尔滨银行展区精彩纷呈
    • 大额解禁撂倒股价 医疗影像龙头跌出千亿俱乐部 葛兰二季度大幅减仓
      大额解禁撂倒股价 医疗影像龙头跌出千亿俱乐部 葛兰二季度大幅减仓
    • A股,又上了热搜!数字要素概念走高多股涨停,锂电池板块走低恩捷股份大举跌停
      A股,又上了热搜!数字要素概念走高多股涨停,锂电池板块走低恩捷股份大举跌停
    • 最新!巨头出手,加仓宁王51%
      最新!巨头出手,加仓宁王51%
    • 600亿巨头暴雷
      600亿巨头暴雷
    • 一天32家!科创板回购潮涌来
      一天32家!科创板回购潮涌来
    • 提振信心实招来了!30余家上市公司密集出手 最高要买10亿
      提振信心实招来了!30余家上市公司密集出手 最高要买10亿
    • 高盛再发50年后预测:2075年印度股市全球市值占比将升4倍 中国升3成
      高盛再发50年后预测:2075年印度股市全球市值占比将升4倍 中国升3成

    新闻资讯栏目

    站长QQ: 2397470084