我如何从交易收据重新创建原始交易以验证 v,r,s 签名?
How can I re create raw transaction from the transaction receipt to verify v,r,s signature?
我正在尝试验证以太坊交易。
这是我的步骤。
1. 进行交易
2. 使用 eth.getTransaction()
获取交易
3. 使用 ethereumjs-tx 重新创建交易
但有时我无法验证交易。
案例 1:在私有测试网上简单发送以太币交易
获取交易明细
{
blockHash: "0x2125539ac67b4569828737ffb1731048e00121954f0555d0dc96af665071a62b",
blockNumber: 24615,
from: "0x81c24515cdf1a4b68f34e3e2824d44b28d00f010",
gas: 90000,
gasPrice: 18000000000,
hash: "0x9e4ce952759eae925173c6c6055c1afe577a48462caacd8d4fb742e911eae053",
input: "0x",
nonce: 0,
r: "0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093",
s: "0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b",
to: "0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a",
transactionIndex: 0,
v: "0x10f469",
value: 1000000000000000000
}
然后用交易细节和 ethereumjs-tx 创建一个 transactin。
const EthereumTx = require('ethereumjs-tx')
const test_arr1 = {
nounce: "0x"+parseInt(0, 10).toString(16),
gasPrice: "0x"+parseInt(18000000000, 10).toString(16),
gasLimit: "0x"+parseInt(90000, 10).toString(16),
to: '0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a',
value: "0x"+parseInt(1000000000000000000, 10).toString(16),
data: '0x',
v: '0x10f469',
r: '0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093',
s: '0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b'
}
const tx = new EthereumTx(test_arr1);
const recoveredAddress = "0x"+tx.getSenderAddress().toString('hex')
recoveredAddress is 0x81c24515cdf1a4b68f34e3e2824d44b28d00f010 which is correct
案例 2:ropsten 测试网中的智能合约
获取交易明细
{
blockHash: "0xead9335751dbdb4a874b2bb48ac15ddafbec6f2ba55a5932bf6ec1a0475166e7",
blockNumber: 3026266,
from: "0x0d6883a0e7071513c7d90a27bf2715bc71ecf107",
gas: 309588,
gasPrice: 18000000000,
hash: "0xe69d8b108af59198857dd5b045769748dbe1ca3ad9bba7dbbb512643b9d85b5a",
input: "0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000",
nonce: 129,
r: "0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66",
s: "0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b",
to: "0xad5e2d5cb93f098423597c891d2f1ed35f904ca1",
transactionIndex: 0,
v: "0x2a",
value: 0
}
然后用交易细节和 ethereumjs-tx 创建一个 transactin。
const EthereumTx = require('ethereumjs-tx')
const test_arr2 = {
nounce: "0x"+parseInt(129, 10).toString(16),
gasPrice: "0x"+parseInt(18000000000, 10).toString(16),
gasLimit: "0x"+parseInt(309588, 10).toString(16),
to: '0xad5e2d5cb93f098423597c891d2f1ed35f904ca1',
value: "0x",
data: '0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000',
v: '0x2a',
r: '0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66',
s: '0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b',
chainId: 3
}
const tx2 = new EthereumTx(test_arr2);
const recoveredAddress = "0x"+tx2.getSenderAddress().toString('hex')
recoveredAddress is 0x9c9d4315824275f545b2e96026a7075f75125b9b which is NOT correct. It should be 0x0d6883a0e7071513c7d90a27bf2715bc71ecf107
这是为什么?
我怎样才能正确地重新创建原始交易?
或者是否有任何其他方式来验证带有 v,r,s 签名的交易?
提前致谢。
如果您使用的是 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) returns (address): recover address associated with the public key from elliptic curve signature, return zero on error
示例:
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"
.
签署交易
我正在尝试验证以太坊交易。
这是我的步骤。
1. 进行交易
2. 使用 eth.getTransaction()
获取交易
3. 使用 ethereumjs-tx 重新创建交易
但有时我无法验证交易。
案例 1:在私有测试网上简单发送以太币交易
获取交易明细
{ blockHash: "0x2125539ac67b4569828737ffb1731048e00121954f0555d0dc96af665071a62b", blockNumber: 24615, from: "0x81c24515cdf1a4b68f34e3e2824d44b28d00f010", gas: 90000, gasPrice: 18000000000, hash: "0x9e4ce952759eae925173c6c6055c1afe577a48462caacd8d4fb742e911eae053", input: "0x", nonce: 0, r: "0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093", s: "0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b", to: "0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a", transactionIndex: 0, v: "0x10f469", value: 1000000000000000000 }
然后用交易细节和 ethereumjs-tx 创建一个 transactin。
const EthereumTx = require('ethereumjs-tx') const test_arr1 = { nounce: "0x"+parseInt(0, 10).toString(16), gasPrice: "0x"+parseInt(18000000000, 10).toString(16), gasLimit: "0x"+parseInt(90000, 10).toString(16), to: '0x487f5eea74ea5f3e94093d8b0501f1d2b0d5310a', value: "0x"+parseInt(1000000000000000000, 10).toString(16), data: '0x', v: '0x10f469', r: '0x826b5348acbec72bab39c5debc8493e34d23b351bc7c20ded25d2a4eed736093', s: '0x2a87e18b22c76d61ce9d6a4d56949afa025f1611aa6bb9fd9d6c502d61f7361b' } const tx = new EthereumTx(test_arr1); const recoveredAddress = "0x"+tx.getSenderAddress().toString('hex') recoveredAddress is 0x81c24515cdf1a4b68f34e3e2824d44b28d00f010 which is correct
案例 2:ropsten 测试网中的智能合约
获取交易明细
{ blockHash: "0xead9335751dbdb4a874b2bb48ac15ddafbec6f2ba55a5932bf6ec1a0475166e7", blockNumber: 3026266, from: "0x0d6883a0e7071513c7d90a27bf2715bc71ecf107", gas: 309588, gasPrice: 18000000000, hash: "0xe69d8b108af59198857dd5b045769748dbe1ca3ad9bba7dbbb512643b9d85b5a", input: "0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000", nonce: 129, r: "0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66", s: "0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b", to: "0xad5e2d5cb93f098423597c891d2f1ed35f904ca1", transactionIndex: 0, v: "0x2a", value: 0 }
然后用交易细节和 ethereumjs-tx 创建一个 transactin。
const EthereumTx = require('ethereumjs-tx') const test_arr2 = { nounce: "0x"+parseInt(129, 10).toString(16), gasPrice: "0x"+parseInt(18000000000, 10).toString(16), gasLimit: "0x"+parseInt(309588, 10).toString(16), to: '0xad5e2d5cb93f098423597c891d2f1ed35f904ca1', value: "0x", data: '0x03e63bdb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000890000000b000000012e507fe5cce2f11c2a520a265f593c08372834fec925f84bbe5a72793ec5096d03fd11970afed8b767adfed60caf3f0c1de0dbda06d48f9afc3661717dbf85641b3f011114d3a41bf16a8d8cc33769aba2abe14efb14487295c80da13b3e333707202d1bdea56f75616202491b4bcc437b6a5b7a79284a08e28bcd0a90e3d87bf10000000000000000000000000000000000000000000000', v: '0x2a', r: '0xdd4fe550275bd35ffd4babf6ac3578575594011f027923046da78a7b179ffb66', s: '0x2584e1f3f36185f6cd9358146f2479dde41dbb85ced5859c845a065cb5bdc42b', chainId: 3 } const tx2 = new EthereumTx(test_arr2); const recoveredAddress = "0x"+tx2.getSenderAddress().toString('hex') recoveredAddress is 0x9c9d4315824275f545b2e96026a7075f75125b9b which is NOT correct. It should be 0x0d6883a0e7071513c7d90a27bf2715bc71ecf107
这是为什么? 我怎样才能正确地重新创建原始交易?
或者是否有任何其他方式来验证带有 v,r,s 签名的交易?
提前致谢。
如果您使用的是 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) returns (address): recover address associated with the public key from elliptic curve signature, return zero on error
示例:
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"
.