如何从交易收据重新创建原始交易以验证 v,r,s 签名?

Posted

技术标签:

【中文标题】如何从交易收据重新创建原始交易以验证 v,r,s 签名?【英文标题】:How can I re create raw transaction from the transaction receipt to verify v,r,s signature? 【发布时间】:2018-09-23 04:00:58 【问题描述】:

我正在尝试验证以太坊交易。

这是我的步骤。 1. 进行交易 2. 使用 eth.getTransaction() 获取交易 3. 使用 ethereumjs-tx 重新创建交易

但有时我无法验证交易。

案例一:私测网简单发送以太币交易

    获取交易详情

    块哈希:“0x2125539ac67b4569828737ffb1731048e00121954f0555d0dc96af665071a62b”, 区块编号:24615, 来自:“0x81c24515cdf1a4b68f34e3e2824d44b28d00f010”, 气体:90000, gasPrice: 18000000000, 哈希:“0x9e4ce952759eae925173c6c6055c1afe577a48462caacd8d4fb742e911eae053”, 输入:“0x”, 随机数:0, r: "0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093", s: "0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b", 至:“0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a”, 交易指数:0, v: "0x10f469", 值:1000000000000000000

    然后使用交易详细信息和 ethereumjs-tx 创建一个交易。

    const EthereumTx = require('ethereumjs-tx') 常量 test_arr1 = 声明:“0x”+parseInt(0, 10).toString(16), gasPrice: "0x"+parseInt(18000000000, 10).toString(16), gasLimit: "0x"+parseInt(90000, 10).toString(16), 至:'0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a', 值:“0x”+parseInt(1000000000000000000, 10).toString(16), 数据:'0x', v: '0x10f469', r: '0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093', s: '0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b' const tx = new EthereumTx(test_arr1); 常量恢复地址 = "0x"+tx.getSenderAddress().toString('hex') 恢复地址是 0x81c24515cdf1a4b68f34e3e2824d44b28d00f010 这是正确的

案例 2:ropsten 测试网中的智能合约

    获取交易详情

    块哈希:“0xead9335751dbdb4a874b2bb48ac15ddafbec6f2ba55a5932bf6ec1a0475166e7”, 区块编号:3026266, 来自:“0x0d6883a0e7071513c7d90a27bf2715bc71ecf107”, 气:309588, gasPrice: 18000000000, 哈希:“0xe69d8b108af59198857dd5b045769748dbe1ca3ad9bba7dbbb512643b9d85b5a”, 输入:“0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000” 随机数:129, r: "0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66", s: "0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b", 至:“0xad5e2d5cb93f098423597c891d2f1ed35f904ca1”, 交易指数:0, v:“0x2a”, 值:0

    然后使用交易详细信息和 ethereumjs-tx 创建一个交易。

    const EthereumTx = require('ethereumjs-tx') 常量 test_arr2 = 宣布:“0x”+parseInt(129, 10).toString(16), gasPrice: "0x"+parseInt(18000000000, 10).toString(16), gasLimit: "0x"+parseInt(309588, 10).toString(16), 至:'0xad5e2d5cb93f098423597c891d2f1ed35f904ca1', 值:“0x”, 数据: '0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000', v: '0x2a', r: '0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66', s: '0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b', 链号:3 const tx2 = new EthereumTx(test_arr2); 常量恢复地址 = "0x"+tx2.getSenderAddress().toString('hex') 恢复地址是 0x9c9d4315824275f545b2e96026a7075f75125b9b 这是不正确的。应该是 0x0d6883a0e7071513c7d90a27bf2715bc71ecf107

这是为什么呢? 如何正确重新创建原始交易?

或者有没有其他方法可以用 v,r,s 签名验证交易?

提前致谢。

【问题讨论】:

【参考方案1】:

如果您使用的是 web3js v1.0,您可以简单地使用web3.eth.accounts.recover。他们文档中的示例:

web3.eth.accounts.recover(
    messageHash: '0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655',
    v: '0x1c',
    r: '0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd',
    s: '0x6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a029'
)
> "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23"

另一种选择是您可以在使用 Solidity 的 ecrecover() 的合约中调用 view 函数。来自Solidity docs:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) 返回(地址):从椭圆曲线签名中恢复与公钥关联的地址,出错返回零

例子:

function verify(bytes prefix, bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) external pure returns(address) 
  bytes32 prefixedHash = keccak256(prefix, msgHash);
  return ecrecover(prefixedHash, v, r, s);

你必须小心前缀。不同的客户端可能使用不同的前缀。例如,geth 使用prefix = "\x19Ethereum Signed Message:\n32" 签署交易。

【讨论】:

感谢您回复我。在这种情况下,msgHash 是什么?似乎 hash 和 blockHash 不是一个...... 这是您签名的消息的 sha3 哈希值。顺便说一句,你原来的方法也没有错。我运行了你签名的 tx 并得到了正确的地址: > signed.getSenderAddress().toString('hex'); '0d6883a0e7071513c7d90a27bf2715bc71ecf107'。可能是因为您在对象中拼错了“nonce”? 是的,你是对的!这是因为我的错字。谢谢!

以上是关于如何从交易收据重新创建原始交易以验证 v,r,s 签名?的主要内容,如果未能解决你的问题,请参考以下文章

从 web3py 发送原始交易:TypeError: <lambda>() 缺少 4 个必需的位置参数:'hash'、'r'、's' 和 'v'

iOS7应用收据中的交易ID何时更改?

在 iOS In-App-Purchases 收据验证上返回许多交易

如何从MKStoreKit获取交易ID和收据?

如何将交易收据与可在 iTunes Connect 中下载的财务报告相关联?

区块链 以太坊 交易中 r,s,v 是什么 作用