本文针对初学者详细介绍了可变合约(metamorphic contracts)的概念和实现,重点讨论如何使用create和create2操作码在同一地址上重新部署合约。文章包含具体代码示例、交易哈希、观察结果以及对合约字节码的深入分析,适合希望了解智能合约更深层次的开发者。
上一篇文章:https://medium.com/coinmonks/dark-side-of-create2-opcode-6b6838a42d71
上一篇文章概述:我们已经部署并销毁了两个合约,并使用 create 和 create2 操作码在相同地址上重新部署它们。
在继续之前,我想回答与上一篇文章相关的一个常见问题。
上一篇文章是想表达什么(非以太坊开发者)?
智能合约如果以这样的方式编写,则完全是可变的。
好了,现在让我们继续。首先,让我们开始将全新代码部署到目标地址。
按上一篇文章的步骤进行操作,直到第 4 步,然后重新部署 CreatorContract,代替目标合约的代码,可以部署任何代码,它仍会在同一地址上部署。
以下是其代码。
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
contract Target {
address public owner;
constructor() {
owner = msg.sender;
}
function approve (address _spender, uint _value) public returns (string memory) {
return "Thug Life BGM playing...";
}
function destroy() public {
selfdestruct(payable(msg.sender));
}
}
交易哈希:
目标合约自销毁 - 0x3cb50d84b48c2ec1b6d0c58a925fe6fb56d515aedbcd843d5e0bb9e562378a20
目标合约部署 - 0xb311cd648cdb08313e90abeefa27b54f68128543f5b484eb515d84ff974a5388
观察:
5. 为什么我们不能用这个替代代理?→ 答案是合约中存储的上一个状态被销毁,这不是我们所需要的。
变形合约:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.1;
contract Factory {
mapping (address => address) _implementations;
event Deployed(address _addr);
function deploy(uint salt, bytes calldata bytecode) public {
bytes memory implInitCode = bytecode; // 为变形合约分配初始化代码。
bytes memory metamorphicCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
); // 确定变形合约的地址。
address metamorphicContractAddress = _getMetamorphicContractAddress(salt, metamorphicCode); // 声明实现合约的地址变量。
address implementationContract; // 加载实现初始化代码和长度,然后通过 CREATE 部署。
/* solhint-disable no-inline-assembly */
assembly {
let encoded_data := add(0x20, implInitCode) // 加载初始化代码。
let encoded_size := mload(implInitCode) // 加载初始化代码的长度。
implementationContract := create( // 使用 3 个参数调用 CREATE。
0, // 不转发任何资金。
encoded_data, // 传入初始化代码。
encoded_size // 传入初始化代码的长度。
)
} /* solhint-enable no-inline-assembly */
// 首先,我们在单独的地址上部署我们要部署的代码
// 存储要通过变形合约检索的实现。
_implementations[metamorphicContractAddress] = implementationContract;
address addr;
assembly {
let encoded_data := add(0x20, metamorphicCode) // 加载初始化代码。
let encoded_size := mload(metamorphicCode) // 加载初始化代码的长度。
addr := create2(0, encoded_data, encoded_size, salt)
}
require(
addr == metamorphicContractAddress,
"Failed to deploy the new metamorphic contract."
);
emit Deployed(addr);
}
/**
* @dev 计算给定特定盐值的变形合约地址的内部视图函数。
*/
function _getMetamorphicContractAddress(
uint256 salt,
bytes memory metamorphicCode
) internal view returns (address) { // 确定变形合约的地址。
return address(
uint160( // 下降到匹配地址类型。
uint256( // 转换为 uint 以截断高位数字。
keccak256( // 通过 4 个输入计算 CREATE2 哈希。
abi.encodePacked( // 将所有输入打包在一起以便计算哈希。
hex"ff", // 以 0xff 开始以区分 RLP。
address(this), // 此合约将是调用者。
salt, // 传入提供的盐值。
keccak256(
abi.encodePacked(
metamorphicCode
)
) // 初始化代码哈希。
)
)
)
)
);
}
// 这两个函数由变形合约调用
function getImplementation() external view returns (address implementation) {
return _implementations[msg.sender];
}
}
contract Test1 {
uint public myUint;
function setUint(uint _myUint) public {
myUint = _myUint;
}
function killme() public {
selfdestruct(payable(msg.sender));
}
}
contract Test2 {
uint public myUint;
function setUint(uint _myUint) public {
myUint = 2 * _myUint;
}
function killme() public {
selfdestruct(payable(msg.sender));
}
}
观察:
2. 创建代码 →
create2
操作码。3. 运行时代码 →
C
的构造函数所部署的代码。C
有一个使用内联汇编的构造函数,这可能与实际部署的字节码不同。.creationCode
的相同限制也适用于此属性。由于上述原因,我目前无法在 polyscan 上验证 create2 变形合约。在下一篇文章中,我将分析
“5860208158601c335a63aaf10f428752fa158151803b80938091923cf3” 这段字节码在 EVM Playground 上以获得更好的背景。
要深入理解,请访问 https://github.com/0age/metamorphic
变形合约工作详细分析在这个仓库中,我将发布下一篇文章,其中将分析这个仓库。(注意:它是一个有良好注释的仓库)
有关此主题的良好文章:
- 原文链接: medium.com/coinmonks/met...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!