Nick 的方法 - 以太坊无密钥执行/部署方法

通过无密钥执行/部署方法,在无需信任的情况下,实现多链上部署同一合约到同一地址的能力。

本文将详细解释“Nick 的方法”,这是一种在各种以太坊改进提案中广泛引用的无密钥执行/部署方法,但却不常被完全解释。

我们将介绍其起源、工作原理及其提供的好处,例如降低信任要求和在同一地址上的多链部署能力。

Nick 是谁?

Nick 的方法以 ENS(以太坊命名服务)首席开发者和以太坊基金会校友 Nick Johnson 命名,因为他为该技术创作了第一个脚本

该方法受到以太坊创始人 Vitalik Buterin 提出的一个想法的启发,即即使一个交易没有通过私钥签名也可以有效。

这就产生了使用术语 “无密钥” 的用法,其中与交易相关的有效性不再关联于私钥。

它是如何工作的?

在解释其工作原理之前,必须了解以太坊交易是如何从高层次上构造的。

交易是如何构造的?

以下代码展示了一个简单的以太坊 0 型交易(遗留类型)

{  
  nonce: "0x00",  
  gasPrice: "0x09184e72a000",  
  gasLimit: "0x27100",  
  to: "0x0000000000000000000000000000000000000000",  
  value: "0x01",  
  data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057,  
}

JSON 字段声明了交互的地址、金额、数据、gas 价格和 gas 限值。

在使用私人密钥签署交易后,我们将得到与交易签名相关的 3 个额外字段(v、r、s):

{  
  nonce: "0x00",  
  gasPrice: "0x09184e72a000",  
  gasLimit: "0x27100",  
  to: "0x0000000000000000000000000000000000000000",  
  value: "0x01",  
  data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057",  
  v: "0x26",  
  r: "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",  
  s: "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663,  
}

在签名时,对主交易字段如 nonce、gasPrice、gasLimit、to、value 和 data 中的任何变动都会导致不同的签名值。

如你所见,交易中没有“from”字段,那么网络如何知道谁在执行交易?从谁的账户中扣减 gas 费和发送的金额呢?

网络将使用“ecrecover”函数作为消息与签名的参数,以从中恢复签署者的公钥 => 地址。

在这一背景下,主要的交易充当了消息的角色,而 v、r 和 s 则为对应的签名。

// 伪代码 :)

// 签署时

const addressA = '0x067024faa81ACBF984EEA0E4E75Fcc3F44558AfD';  
const addressAPrvtKey = 'b37ff986840fc14c956d74a7a3375488cce495d7fe36cfa5e8201df1f1a03aaf';  

const {v,r,s} = sign(Transaction, addressAPrvtKey);  

> v: 0x25  
> r: 0x79855f28bdc327adbcbf85d32cb76b9aeef67dc5b9c4dafbd0a94ad3757ec501  
> s: 0x3a51556f88edc2e218c5b6c540662bf289a09f16e3a0f505fcfe435dfb490a22  

// 恢复时

const addressRecovered = ecrecover(Transaction, v, r, s);  

> addressRecovered: 0x067024faa81ACBF984EEA0E4E75Fcc3F44558AfD

如上面代码所示,地址 A 正在签署交易。当交易提交至网络时,它将利用“ecrecover”函数,对交易及相关联的签名参数进行操作,以识别地址 A 是该交易的发送者,并负责支付 gas 费用等。

谁将交易转发给网络并不重要,因为如前所述,执行交易的地址是从交易及其签名中恢复出来的地址,并不一定是将交易转交(广播)给网络的人。

交易篡改

由于生成交易的地址是从交易本身及其签名字段中恢复的,那如果我们篡改其中一个签名参数会发生什么?

事实证明,“ecrecover”在多数随机生成的_v,r,s_值上都表现良好,并会返回一个与消息和签名对应的有效以太坊地址。

// 伪代码 :)

如果将 v,r,s 更改为:

> v: 0x25  
> r: 0x1212121212121212121212121212121212121212121212121212121212121212 // 人类生成 & 随机  
> s: 0x0000000000000000000000000000000001000000000000000000000000000000 // 人类生成 & 随机  

// 恢复时

const addressRecovered = ecrecover(Transaction, v, r, s);  

// 返回一个随机的以太坊地址

> addressRecovered: 0xe2140bdbe71cdf1d1df3a6b5d85939d1ad313722

通常情况下,每当一个地址使用与之相关的私钥签署消息时,都会生成 v、r 和 s 值。

尽管恢复的地址没有亲自签署该交易,但使用这些随机生成的签名值的交易(消息)将返回其地址(恢复的地址)。

从恢复的地址执行

即使 v、r 和 s 值更改为随机生成的值,该交易仍不会被视为“无效”。只要从交易及其签名中恢复的地址中有足够的资金来支付 gas 费用,交易将仍然会从该恢复的地址成功执行。

在这个情况下,解决办法是手动为恢复的地址提供一些以太坊,以便从其余额中扣除 gas 费用。

在为恢复的地址提供资金后,并把交易广播至网络,交易将从恢复的地址成功执行。

恢复的地址被其他人使用的概率(用一个已知的私钥控制)极其低,这使得为其提供资金并假定它的 nonce 是安全的。

总结

总的来说,这种方法允许不通过私钥签署来执行交易(无密钥),从而仅为从其执行一次性交易生成一个“单次使用的不可控地址”。

实际用例

起初,很难预见这种方法会如何应用。为什么有人会从一个不可控的地址执行交易?为什么不直接从他的主地址呢?

最小化信任

如 Nick 的文章所述, 如何向 11,440 人发送以太币 ,这种方法被用于在 DAO 黑客事件后向区块链的 11,440 个地址发送以太币。

由于资金由多重签名控制,每次需要从多签转账到某个地址时,要求多个签名是非常困难且资源消耗大的。这可能会耗费很长时间,并消耗大量 gas。

将资金发送到一个受控地址并信任该地址将以太发送给其他 11,440 个地址不是一个选项。

他们会花一周时间仔细签署 11,440 个单独的交易吗?他们会将全部金额发送到一个由单个个人控制的账户,以便他们可以进行支付吗?

可以以一种无需信任的方式进行所有转账,只需要受托人进行一次转账——Nick Johnson

简而言之,解决方案是创建一个 MultiSend 合约,该合约将一组值(以太)发送到一组接收者:

  • 要达到 11,140 个地址,总共需要 104 个不同的交易。
    生成 104 个与 MultiSend 交互并向每个约 110 个地址发送以太的交易(由于 gas 限制)。
  • 弄乱生成的交易的签名。
  • 使用 “ecrecover” 恢复 104 个不受控制的地址。
  • 生成一个再次与 MultiSend 交互的交易,为 104 个地址提供以太。
  • 修改签名,然后恢复一个不受控制的地址。
  • 将所有需要的资金从多签名地址发送到最后恢复的不受控制的地址。
  • 广播从最后恢复的不受控制的地址向网络发送 104 个地址的交易。
  • 广播从 104 个地址向网络发送 110 个单独地址的交易。

如何向 11,440 人发送以太——Nick Johnson

..启动整个过程,最终将以太发送给列表上的每个人——只需要一个签名,并且无需信任个人处理资金。

在同一地址上进行多链部署

合约地址是基于两个因素生成的:发送者的地址和他的 nonce。

因此,技术上可以通过在不同网络上使用相同的 nonce 从同一地址广播合约部署交易来在同一地址部署合约,即使不使用 Nick 的方法。然而,Nick 的方法提供了哪些该方法没有的好处?

首先,这种部署方式的问题是:

  • 需要维护和手动操作,因此需要有人安全地保存控制地址的私钥。
  • 有可能弄乱 nonce,导致 nonce 高于用于部署的 nonce,从而失去在同一地址部署合约的可能性。

注册表和工厂通常是需要在多个链上以相同地址部署的合约类型。这需要安全地保存私钥,以确保注册表或工厂在未来可能创建的不同链上具有相同的地址。

随着智能合约账户和钱包的日益普及,越来越常见的是在多个网络上以相同地址部署账户和钱包。为了实现这一点,需要在不同链上以相同地址部署 CREATE2 工厂合约。

Nick 的方法提供了一种简单的解决方案,通过从不受控制的地址向网络发送交易,在多个链上以相同地址部署合约。

步骤如下:

  • 生成类型为 0 的交易——Legacy。
{  
  nonce: "0x00",  
  gasPrice: "0x09184e72a000",  
  gasLimit: "0x27100",  
  value: "0x00",  
  data: "0x<合约的字节码>",  
}
  • 添加值为“0x1b”(27)的 v 字段。(稍后你会明白原因👀)
  • 为 r, s 添加随机值。
{  
  nonce: "0x00",  
  gasPrice: "0x09184e72a000",  
  gasLimit: "0x27100",  
  value: "0x00",  
  data: "0x<合约的字节码>",  
  v: "0x1b",  
  r: "0x0000000001000000000000000000000001000000000000000000000000100000",  
  s: "0x1212121212121212121212121212121212121212121212121212121212121212",  

}
  • 使用“ecrecover”恢复部署者的地址。
  • 定序交易以生成准备在以太坊网络上广播的原始交易。

你将拥有 rawTx、部署者地址,还可以获取将创建的合约地址。

  • 你为部署者地址提供 gasPrice * gasLimit。
  • 连接到不同的网络并广播 rawTx。
const Web3 = require("web3");  
const web3 = new Web3(/** 连接到不同的网络 */);  

const rawTx = '0xf9074b808506400000008307a1208080b906f8600560005560c0604052601460809081527f53696d706c6520436f6e7472616374204e616d6500000000000000000000000060a05260019061004190826100f3565b5034801561004e57600080fd5b506101b2565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168061007e57607f821691505b60208210810361009e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156100ee57600081815260208120601f850160051c810160208610156100cb5750805b601f850160051c820191505b818110156100ea578281556001016100d7565b5050505b505050565b81516001600160401b0381111561010c5761010c610054565b6101208161011a845461006a565b846100a4565b602080601f831160018114610155576000841561013d5750858301515b600019600386901b1c1916600185901b1785556100ea565b600085815260208120601f198616915b8281101561018457888601518255948401946001909101908401610165565b50858210156101a25787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610537806101c16000396000f3fe6080604052600436106100655760003560e01c8063c47f002711610043578063c47f0027146100ca578063f2c9ecd8146100ea578063f38fb65b1461010857600080fd5b806317d7de7c1461006a5780633fb5c1cb14610095578063568a1c69146100b7575b600080fd5b34801561007657600080fd5b5061007f61011d565b60405161008c9190610283565b60405180910390f35b3480156100a157600080fd5b506100b56100b03660046102d8565b600055565b005b6100b56100c5366004610307565b6101af565b3480156100d657600080fd5b506100b56100e5366004610307565b610205565b3480156100f657600080fd5b5060005460405190815260200161008c565b34801561011457600080fd5b506100b5610215565b60606001805461012c906103b8565b80601f0160208091040260200160405190810160405280929190818152602001828054610158906103b8565b80156101a55780601f1061017a576101008083540402835291602001916101a5565b820191906000526020600020905b81548152906001019060200180831161018857829003601f168201915b5050505050905090565b60323410156102055760405162461bcd60e51b815260206004820152601960248201527f4e6f7420656e6f7567682076616c75652070726f76696465640000000000000060448201526064015b60405180910390fd5b60016102118282610441565b5050565b60405162461bcd60e51b815260206004820152603660248201527f546172676574436f6e74726163743a72657665727443616c6c3a20746869732060448201527f66756e6374696f6e20686173207265766572746564210000000000000000000060648201526084016101fc565b600060208083528351808285015260005b818110156102b057858101830151858201604001528201610294565b818111156102c2576000604083870101525b50601f01601f1916929092016040019392505050565b6000602082840312156102ea57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561031957600080fd5b813567ffffffffffffffff8082111561033157600080fd5b818401915084601f83011261034557600080fd5b813581811115610357576103576102f1565b604051601f8201601f19908116603f0116810190838211818310171561037f5761037f6102f1565b8160405282815287602084870101111561039857600080fd5b826020860160208301376000928101602001929092525095945050505050565b600181811c908216806103cc57607f821691505b6020821081036103ec57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561043c57600081815260208120601f850160051c810160208610156104195750805b601f850160051c820191505b8181101561043857828155600101610425565b5050505b505050565b815167ffffffffffffffff81111561045b5761045b6102f1565b61046f8161046984546103b8565b846103f2565b602080601f8311600181146104a4576000841561048c5750858301515b600019600386901b1c1916600185901b178555610438565b600085815260208120601f198616915b828110156104d3578886015182559484019460019091019084016104b4565b50858210156104f15787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220e33436a76b28a7ded5996fad916166dc8b63f86b65336f728d944e1b9252427b64736f6c634300080f00331ba0b0506c61293520454796fae142b4e173251fa5d91a7d6506cc0ca0669708065da00cd51f3367e8d81c6afa79ea89521b6b8dd777d23d26535bad37bc8d83f39c70'

async function sendRawTx() {  
    await web3.eth.sendSignedTransaction(rawTx)  
    .on('receipt', console.log);  
}  

sendRawTx();

那么,为什么我们通过这种方法会得到相同的地址?主要是因为恢复的地址是不可控的,这意味着在所有链上的 nonce 当前都是 0。当该地址部署合约时,所部署合约的地址取决于部署者的 nonce(0,在所有链上相同)和部署者的地址(在所有链上相同)。通过这种方式,我们可以确保要创建的合约将在所有链上部署在相同的地址上。

使用这种方法,无需保护私钥或担心 nonce 是特定值,因为对于不可控的地址,它总是为零。原始交易可以公开,任何想要部署合约的人只需为部署者的地址提供所需的费用并将原始交易广播到网络即可。

也可以尝试不同的 rs 值组合,以便将要创建的合约地址变为具有特殊字符的定制地址,如 0xFaC100450Af66d838250EA25a389D8Cd090626290xFaC10Factory 开头。

EIP-155 和 ChainId

EIP-155 引入了 chainId 作为一种防止交易重放攻击的措施,确保交易仅在预期的区块链网络上执行,类似于 nonces 防止交易在同一链上多次重复。

示例:Bob 在以太坊上有 10 个以太币,在 LUKSO 上有 10 个 LYX,想要发送给 Alice 3 个以太币。如果他构建了不包含重放保护(chainId)的交易,Alice 可以在 LUKSO 上重新广播相同的交易,并从 Bob 的地址接收 3 个 LYX。

chainId 包含在签名的 v 值中,使用以下公式:

v = CHAIN_ID * 2 + 35 || v = CHAIN_ID * 2 + 36

这对于应用 Nick 的方法的特定用例(在相同地址上进行多链部署)来说是有问题的,因为交易需要在多个链上广播而无需进行任何修改,包括 v 值。如果 v 值发生变化,它将改变签名,随后改变从交易和签名中恢复的部署者地址,导致每个链上的合约地址不同,从而破坏了跨不同链在相同地址上部署的目的。

为解决此问题,并避免 v 值与特定链相关联,以及实现多链交易而不受 chainId 影响,交易可以在不包含 chainId 并且 v 值为 27(十六进制为 0x1b)的情况下执行。这一 v 值 27 是 EIP-155 实施之前用于交易的值。

边缘案例

使用 Nick 的方法进行 同一地址的多链部署 时,需要记住生成的原始交易不能在事后进行修改。这是因为对交易所做的任何更改都会影响生成的合约和部署者地址。

因此,在指定交易中的 gasPrice 值时考虑未来 gas 价格是尤为重要的。建议将 gasPrice 设置为一个较高的值,以支持其他链上的潜在 gas 价格上涨。因此,如果你将交易中的 gasPrice 设置为 100 gwei,而将来在链 B 上 gasPrice 为 150,在链 B 上发送生成的 rawTx 将导致回滚。

此外,需要注意的是,有些网络节点可能会阻止不使用 EIP155 保护的交易,这也可能是一个需要考虑的限制。因此,需要在节点级别上进行更多工作以允许这种交易,并使用 rpc.allow-unprotected-txs 标志。

使用“非保护交易”一词是为了强调这样一点:如果 v 中未包含 chainId,则可能在其他网络上重放此交易。对于我们的用例,这是一个有意的行为,但对于其他场景,它可能是有害的。就像上文的 Alice 和 Bob 的例子。

NPM 包

鉴于使用_Nick 的方法_的文档和工具有限,我编写了这篇文章和一个 npm 包,以便于生成此类交易。

Nick-Method 生成 EVM 交易的 NPM 包

通过 Twitter, Github, 以及 LinkedIn 与我联系。

参考资料:

我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Yamen Merhi
Yamen Merhi
Smart Contracts - Research Engineer