向派生函数添加属性
🚧 OP Stack Hacks 是一些明确不适用于生产环境的 OP Stack 技巧
OP Stack Hacks 不适合新手。您将无法获得针对 OP Stack Hacks 的重要开发者支持 - 请准备好自己动手并在没有支持的情况下工作。
# 概述
在本指南中,我们将修改 Bedrock Rollup。虽然有很多修改 OP Stack 的方法,但在本教程中,我们将专注于修改派生函数。具体而言,我们将更新派生函数以跟踪在 L1 上燃烧的 ETH 的数量!谁会告诉 ultrasound.money (opens new window) 他们应该用 OP Stack 链替换后端呢?
# 获取灵感
让我们快速回顾一下我们即将做的事情。op-node
负责生成引擎 API 负载,以触发 op-geth
生成区块和交易。op-node
已经为每个 L1 块生成了一个“系统交易”,用于向 L2 链传递有关当前 L1 状态的信息。我们将修改 op-node
,添加一个新的系统交易,报告每个块中的总燃烧金额(基础费用乘以使用的 gas)。
虽然听起来可能很复杂,但整个过程只涉及部署一个智能合约,向 op-node
添加一个新文件,并修改 op-node
中的一个现有文件。这将是无痛的。让我们开始吧!
# 部署燃烧合约
我们将在 Rollup 上使用一个智能合约来存储 op-node
对 L1 燃烧的报告。以下是我们智能合约的代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title L1Burn
* @notice L1Burn keeps track of the total amount of ETH burned on L1.
*/
contract L1Burn {
/**
* @notice Total amount of ETH burned on L1.
*/
uint256 public total;
/**
* @notice Mapping of blocks numbers to total burn.
*/
mapping (uint64 => uint256) public reports;
/**
* @notice Allows the system address to submit a report.
*
* @param _blocknum L1 block number the report corresponds to.
* @param _burn Amount of ETH burned in the block.
*/
function report(uint64 _blocknum, uint64 _burn) external {
require(
msg.sender == 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001,
"L1Burn: reports can only be made from system address"
);
total += _burn;
reports[_blocknum] = total;
}
/**
* @notice Tallies up the total burn since a given block number.
*
* @param _blocknum L1 block number to tally from.
*
* @return Total amount of ETH burned since the given block number;
*/
function tally(uint64 _blocknum) external view returns (uint256) {
return total - reports[_blocknum];
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
将这个智能合约部署到您的 L2(使用任何您方便的工具)。记下合约部署的地址,因为您一会儿会用到它。简单!
# 添加燃烧交易
现在我们需要在 op-node
中添加逻辑,以便在生成 L1 块时自动提交燃烧报告。由于这个交易与报告其他 L1 块信息的系统交易非常相似(在 l1_block_info.go (opens new window) 中找到),我们将使用该交易作为起点。
导航到
op-node
包:cd ~/optimism/op-node
1在
rollup/derive
文件夹中创建一个名为l1_burn_info.go
的新文件:touch rollup/derive/l1_burn_info.go
1将以下内容粘贴到
l1_burn_info.go
文件中,并确保将YOUR_BURN_CONTRACT_HERE
替换为您刚刚部署的L1Burn
合约的地址。package derive import ( "bytes" "encoding/binary" "fmt" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum-optimism/optimism/op-node/eth" ) const ( L1BurnFuncSignature = "report(uint64,uint64)" L1BurnArguments = 2 L1BurnLen = 4 + 32*L1BurnArguments ) var ( L1BurnFuncBytes4 = crypto.Keccak256([]byte(L1BurnFuncSignature))[:4] L1BurnAddress = common.HexToAddress("YOUR_BURN_CONTRACT_HERE") ) type L1BurnInfo struct { Number uint64 Burn uint64 } func (info *L1BurnInfo) MarshalBinary() ([]byte, error) { data := make([]byte, L1BurnLen) offset := 0 copy(data[offset:4], L1BurnFuncBytes4) offset += 4 binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Number) offset += 32 binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Burn) return data, nil } func (info *L1BurnInfo) UnmarshalBinary(data []byte) error { if len(data) != L1InfoLen { return fmt.Errorf("data is unexpected length: %d", len(data)) } var padding [24]byte offset := 4 info.Number = binary.BigEndian.Uint64(data[offset+24 : offset+32]) if !bytes.Equal(data[offset:offset+24], padding[:]) { return fmt.Errorf("l1 burn tx number exceeds uint64 bounds: %x", data[offset:offset+32]) } offset += 32 info.Burn = binary.BigEndian.Uint64(data[offset+24 : offset+32]) if !bytes.Equal(data[offset:offset+24], padding[:]) { return fmt.Errorf("l1 burn tx burn exceeds uint64 bounds: %x", data[offset:offset+32]) } return nil } func L1BurnDepositTxData(data []byte) (L1BurnInfo, error) { var info L1BurnInfo err := info.UnmarshalBinary(data) return info, err } func L1BurnDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig) (*types.DepositTx, error) { infoDat := L1BurnInfo{ Number: block.NumberU64(), Burn: block.BaseFee().Uint64() * block.GasUsed(), } data, err := infoDat.MarshalBinary() if err != nil { return nil, err } source := L1InfoDepositSource{ L1BlockHash: block.Hash(), SeqNumber: seqNumber, } return &types.DepositTx{ SourceHash: source.SourceHash(), From: L1InfoDepositerAddress, To: &L1BurnAddress, Mint: nil, Value: big.NewInt(0), Gas: 150_000_000, IsSystemTransaction: true, Data: data, }, nil } func L1BurnDepositBytes(seqNumber uint64, l1Info eth.BlockInfo, sysCfg eth.SystemConfig) ([]byte, error) { dep, err := L1BurnDeposit(seqNumber, l1Info, sysCfg) if err != nil { return nil, fmt.Errorf("failed to create L1 burn tx: %w", err) } l1Tx := types.NewTx(dep) opaqueL1Tx, err := l1Tx.MarshalBinary() if err != nil { return nil, fmt.Errorf("failed to encode L1 burn tx: %w", err) } return opaqueL1Tx, nil }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103如果你感兴趣,可以随意查看这个文件。它相对简单,主要是定义了一个新的交易类型,并描述了如何对该交易进行编码。
# 插入燃烧交易
最后,我们需要更新 ~/optimism/op-node/rollup/derive/attributes.go
文件,将新的燃烧交易插入到每个区块中。您需要进行以下更改:
找到以下代码行:
l1InfoTx, err := L1InfoDepositBytes(seqNumber, l1Info, sysConfig) if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) }
1
2
3
4在这些代码行之后,添加以下代码片段:
l1BurnTx, err := L1BurnDepositBytes(seqNumber, l1Info, sysConfig) if err != nil { return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) }
1
2
3
4紧接着,更改以下行:
txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)) txs = append(txs, l1InfoTx)
1
2to
txs := make([]hexutil.Bytes, 0, 2+len(depositTxs)) txs = append(txs, l1InfoTx) txs = append(txs, l1BurnTx)
1
2
3
我们在这里做的只是在每个 l1InfoTx
之后创建一个新的燃烧交易,并将其插入到每个区块中。
# 重新构建您的 op-node
在我们能够看到这个变化生效之前,您需要重新构建您的 op-node
:
cd ~/optimism/op-node
make op-node
2
现在启动您的 op-node
,如果它没有运行,请重新启动您的 op-node
,如果它已经在运行中。您应该立即看到变化 - 新的区块将包含两个系统交易而不仅仅是一个!
# 检查结果
查询您的合约的 total
函数,您应该看到总数慢慢增加。尝试使用 tally
函数获取自某个 L2 区块以来燃烧的 gas 数量。您可以使用这个功能来实现一个以 OP Stack 作为后端的 ultrasound.money (opens new window) 版本。我们做到了,Reddit!
获取总数的一种方法是运行以下命令:
export ETH_RPC_URL=http://localhost:8545
cast call <YOUR_BURN_CONTRACT_HERE> "total()" | cast --from-wei
2
# 结论
通过对 op-node
进行一些微小的更改,您刚刚实现了对 OP Stack 的改变,使您能够跟踪 L1 ETH 在 L2 上的燃烧情况。通过一个实时的 Cannon 容错系统,您不仅能够在 L2 上跟踪 L1 的燃烧情况,还能够向 L1 上的合约 证明 这种燃烧。您可以在燃烧的 ETH 数量上建立一个无需信任的预测市场。这太疯狂了!
OP Stack 是一个非常强大的平台,可以让您以无需信任的方式执行大量计算。对于智能合约来说,这是一个超能力。跟踪 L1 的燃烧只是您可以在 OP Stack 上做的许多疯狂事情中的一种。如果您正在寻找灵感,或者想看看其他人在 OP Stack 上构建了什么,可以查看我们的 OP Stack Hacks 页面。也许您会找到一个您想要参与的项目,或者您会获得建立下一个杀手级智能合约所需的灵感。