问题:内存数据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字节存储的是参数的长度