zoukankan      html  css  js  c++  java
  • solidity 汇编语言问题——内存数据mload时为什么从第32位开始

     问题:内存数据mload时为什么从第32位开始

    代码出处:https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d

    pragma solidity ^0.4.24;
    library ECVerify {
        // Duplicate Solidity's ecrecover, but catching the CALL return value
        function safer_ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address) {
            // We do our own memory management here. Solidity uses memory offset
            // 0x40 to store the current end of memory. We write past it (as
            // writes are memory extensions), but don't update the offset so
            // Solidity will reuse it. The memory used here is only needed for
            // this context.
    
            // FIXME: inline assembly can't access return values
            bool ret;
            address addr;
    
            assembly {
                let size := mload(0x40)
                mstore(size, hash)
                mstore(add(size, 32), v)
                mstore(add(size, 64), r)
                mstore(add(size, 96), s)
    
                // NOTE: we can reuse the request memory because we deal with
                //       the return code
                ret := call(3000, 1, 0, size, 128, size, 32)
                addr := mload(size)
            }
    
            return (ret, addr);
        }
    
        function ecrecovery(bytes32 hash, bytes sig) public returns (bool, address) {
            bytes32 r;
            bytes32 s;
            uint8 v;
    
            if (sig.length != 65)
              return (false, 0);
    
            // The signature format is a compact form of:
            //   {bytes32 r}{bytes32 s}{uint8 v}
            // Compact means, uint8 is not padded to 32 bytes.
            assembly {
                r := mload(add(sig, 32))//????????????
                s := mload(add(sig, 64))
    
                // Here we are loading the last 32 bytes. We exploit the fact that
                // 'mload' will pad with zeroes if we overread.
                // There is no 'mload8' to do this, but that would be nicer.
                v := byte(0, mload(add(sig, 96)))
    
                // Alternative solution:
                // 'byte' is not working due to the Solidity parser, so lets
                // use the second best option, 'and'
                // v := and(mload(add(sig, 65)), 255)
            }
    
            // albeit non-transactional signatures are not specified by the YP, one would expect it
            // to match the YP range of [27, 28]
            //
            // geth uses [0, 1] and some clients have followed. This might change, see:
            //  https://github.com/ethereum/go-ethereum/issues/2053
            if (v < 27)
              v += 27;
    
            if (v != 27 && v != 28)
                return (false, 0);
    
            return safer_ecrecover(hash, v, r, s);
        }
    
        function ecverify(bytes32 hash, bytes sig, address signer) public returns (bool,address) {
            bool ret;
            address addr;
            (ret, addr) = ecrecovery(hash, sig);
            return (ret == true && addr == signer,addr);
        }
    }
    
    contract ECVerifyTest {
        function test_v0() public returns (bool,address) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xacxa7xdax99x7axd1x77xf0x40x24x0cxdcxcfx69x05xb7x1axb1x6bx74x43x43x88xc3xa7x2fx34xfdx25xd6x43x93x46xb2xbaxc2x74xffx29xb4x8bx3exa6xe2xd0x4cx13x36xeaxcexafxdax3cx53xabx48x3fxc3xffx12xfaxc3xebxf2x00";
            return ECVerify.ecverify(hash, sig, 0x0E5cB767Cce09A7F3CA594Df118aa519BE5e2b5A);
        }
    
        function test_v1() public returns (bool,address) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xdexbaxaax0cxddxb3x21xb2xdcxaaxf8x46xd3x96x05xdex7bx97xe7x7bxa6x10x65x87x85x5bx91x06xcbx10x42x15x61xa2x2dx94xfax8bx8ax68x7fxf9xc9x11xc8x44xd1xc0x16xd1xa6x85xa9x16x68x58xf9xc7xc1xbcx85x12x8axcax01";
            return ECVerify.ecverify(hash, sig, 0x8743523D96A1B2CbE0c6909653a56da18ed484Af);
        }
    }

    每当使用出现了使用到汇编语言时,我就一直很奇怪一个问题,就是r := mload(add(sig, 32))这里为什么要加32,后面通过测试发现,前32个字节存储的是数据的长度

    测试过程:

    1)先得到原代码取得的(v, r, s)

    pragma solidity ^0.4.24;
    library ECVerify {
        function ecrecovery(bytes32 hash, bytes sig) public returns (uint8, bytes32, bytes32) {
            bytes32 r;
            bytes32 s;
            uint8 v;
    
            if (sig.length != 65)
              return (v, r, s);
    
            // The signature format is a compact form of:
            //   {bytes32 r}{bytes32 s}{uint8 v}
            // Compact means, uint8 is not padded to 32 bytes.
            assembly {
                r := mload(add(sig, 32))
                s := mload(add(sig, 64))
    
                // Here we are loading the last 32 bytes. We exploit the fact that
                // 'mload' will pad with zeroes if we overread.
                // There is no 'mload8' to do this, but that would be nicer.
                v := byte(0, mload(add(sig, 96)))
    
                // Alternative solution:
                // 'byte' is not working due to the Solidity parser, so lets
                // use the second best option, 'and'
                // v := and(mload(add(sig, 65)), 255)
            }
    
            // albeit non-transactional signatures are not specified by the YP, one would expect it
            // to match the YP range of [27, 28]
            //
            // geth uses [0, 1] and some clients have followed. This might change, see:
            //  https://github.com/ethereum/go-ethereum/issues/2053
            if (v < 27)
              v += 27;
    
            if (v != 27 && v != 28)
                return (v, r, s);
    
            return (v, r, s);
        }
    
    }
    
    contract ECVerifyTest {
        function test_v0() public returns (uint8,bytes32, bytes32) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xacxa7xdax99x7axd1x77xf0x40x24x0cxdcxcfx69x05xb7x1axb1x6bx74x43x43x88xc3xa7x2fx34xfdx25xd6x43x93x46xb2xbaxc2x74xffx29xb4x8bx3exa6xe2xd0x4cx13x36xeaxcexafxdax3cx53xabx48x3fxc3xffx12xfaxc3xebxf2x00";
            return ECVerify.ecrecovery(hash, sig);
        }
    
        function test_v1() public returns(uint8,bytes32, bytes32) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xdexbaxaax0cxddxb3x21xb2xdcxaaxf8x46xd3x96x05xdex7bx97xe7x7bxa6x10x65x87x85x5bx91x06xcbx10x42x15x61xa2x2dx94xfax8bx8ax68x7fxf9xc9x11xc8x44xd1xc0x16xd1xa6x85xa9x16x68x58xf9xc7xc1xbcx85x12x8axcax01";
            return ECVerify.ecrecovery(hash, sig);
        }
    }

    返回:

    2)然后调用0位置的数据:

    pragma solidity ^0.4.24;
    library ECVerify {
    
        function ecrecovery(bytes32 hash, bytes sig) public returns (uint8, bytes32, bytes32) {
            bytes32 r;
            bytes32 s;
            uint8 v;
    
            if (sig.length != 65)
              return (v, r, s);
    
            // The signature format is a compact form of:
            //   {bytes32 r}{bytes32 s}{uint8 v}
            // Compact means, uint8 is not padded to 32 bytes.
            assembly {
                r := mload(add(sig, 0))
                s := mload(add(sig, 32))
    
                // Here we are loading the last 32 bytes. We exploit the fact that
                // 'mload' will pad with zeroes if we overread.
                // There is no 'mload8' to do this, but that would be nicer.
                v := byte(0, mload(add(sig, 64)))
    
                // Alternative solution:
                // 'byte' is not working due to the Solidity parser, so lets
                // use the second best option, 'and'
                // v := and(mload(add(sig, 65)), 255)
            }
    
            return (v, r, s);
        }
    
    }
    
    contract ECVerifyTest {
        function test_v0() public returns (uint8,bytes32, bytes32) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xacxa7xdax99x7axd1x77xf0x40x24x0cxdcxcfx69x05xb7x1axb1x6bx74x43x43x88xc3xa7x2fx34xfdx25xd6x43x93x46xb2xbaxc2x74xffx29xb4x8bx3exa6xe2xd0x4cx13x36xeaxcexafxdax3cx53xabx48x3fxc3xffx12xfaxc3xebxf2x00";
            return ECVerify.ecrecovery(hash, sig);
        }
    
        function test_v1() public returns(uint8,bytes32, bytes32) {
            bytes32 hash = 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad;
            bytes memory sig = "xdexbaxaax0cxddxb3x21xb2xdcxaaxf8x46xd3x96x05xdex7bx97xe7x7bxa6x10x65x87x85x5bx91x06xcbx10x42x15x61xa2x2dx94xfax8bx8ax68x7fxf9xc9x11xc8x44xd1xc0x16xd1xa6x85xa9x16x68x58xf9xc7xc1xbcx85x12x8axcax01";
            return ECVerify.ecrecovery(hash, sig);
        }
    }

    返回结果:

    0x41 = 65,所以能够得出结论,前32字节存储的是参数的长度

  • 相关阅读:
    Linux安装oracle 10g常见问题之——ORA-01078,LRM-00109,ORA-01102
    Linux安装oracle 10g常见问题之——OUI-25031
    C#中static静态变量的用法
    让DIV中的内容水平和垂直居中
    json对象与json字符串互换
    AJAX请求 $.post方法的使用
    .NET(c#)new关键字的三种用法
    创建数据库和表的SQL语句
    SQL、LINQ、Lambda 三种用法(转)
    AJAX中UPDATEPANEL配合TIMER控件实现局部无刷新
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/9592807.html
Copyright © 2011-2022 走看看