Alert Source Discuss
Standards Track: Networking

EIP-6122: 基于时间戳的 Forkid 检查

修改 forkid 检查以使用时间戳和区块号

Authors Marius van der Wijden (@MariusVanDerWijden)
Created 2022-12-13
Requires EIP-2124

摘要

EIP-2124 提出了一种基于 forkid 参数通过其链配置来识别 p2p 网络上的节点的方法。 它允许节点快速切断不兼容的节点,从而使 P2P 网络更加可靠。 合并后,分叉由区块时间而不是区块号安排。此 EIP 使用区块时间更新了 forkid 的计算。

动机

虽然在工作量证明中,分叉是由区块号安排的,但权益证明共识层通过 slot 号安排分叉。 slot 号是基于时间的度量。为了在共识层和执行层上同时安排分叉,在合并后,执行层也被迫按时间戳安排分叉。

forkid 计算允许对等方快速确定对等方的配置并断开配置错误或为其他网络配置的对等方。

规范

每个节点维护以下值:

  • FORK_HASH:已通过的创世哈希和分叉区块号或时间戳的 IEEE CRC32 校验和([4]byte)。
    • 分叉区块号或时间戳按升序馈入 CRC32 校验和。
    • 如果在同一区块或时间应用了多个分叉,则仅对区块号或时间戳进行一次校验和。
    • 区块号被视为 uint64 整数,在进行校验和时以大端格式编码。
    • 区块时间戳被视为 uint64 整数,在进行校验和时以大端格式编码。
    • 如果链配置为从创世之初就以非 Frontier 规则集启动,则不将其视为分叉。
  • FORK_NEXT:下一个即将到来的分叉的区块号或时间戳(uint64),如果不知道下一个分叉,则为 0
    • 请注意,区分 FORK_NEXT 的时间戳还是区块并不重要。

在 homestead 之上,基于时间戳的分叉 1668000000FORK_HASH 将是:

  • forkhash₁ = 0xcb37b2ee (homestead+虚构分叉) = CRC32(<genesis-hash> || uint64(1150000) || uint64(1668000000))

附加规则

应用以下附加规则:

  • 基于时间戳的分叉必须安排在按区块分叉的时间或之后(在主网和私有网络上)。
  • 远程对等方的 forkid 验证的实现需要先按区块然后按时间戳来过滤传入的 forkid。

理由

上海将按时间戳安排,因此需要更新 forkid 计算以使用时间戳和区块。 由于所有基于区块号的分叉都在基于时间的分叉之前,因此节点需要在基于时间的分叉之前检查基于区块的分叉。

向后兼容性

此更改稍微修改了 forkid 的计算。 因此,应用此更改的节点会在时间戳计划的分叉发生后立即删除未应用此更改的对等方。 这不仅是预期的,而且实际上是 forkid 的首要目的。

测试用例

这是一套测试,其中包含主网配置,并在时间 1668000000 启用了提款,并在区块 18000000 处合并了网络拆分区块

type testcase struct {
	head uint64
	want ID
}
tests := []struct {
	config  *params.ChainConfig
	genesis common.Hash
	cases   []testcase
}{
	// Withdrawal test cases
	&withdrawalConfig,
	params.MainnetGenesisHash,
	[]testcase{
		{0, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}},           // 未同步
		{1149999, 0, ID{Hash: checksumToBytes(0xfc64ec04), Next: 1150000}},     // 最后一个 Frontier 区块
		{1150000, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}},     // 第一个 Homestead 区块
		{1919999, 0, ID{Hash: checksumToBytes(0x97c2c34c), Next: 1920000}},     // 最后一个 Homestead 区块
		{1920000, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}},     // 第一个 DAO 区块
		{2462999, 0, ID{Hash: checksumToBytes(0x91d1f948), Next: 2463000}},     // 最后一个 DAO 区块
		{2463000, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}},     // 第一个 Tangerine 区块
		{2674999, 0, ID{Hash: checksumToBytes(0x7a64da13), Next: 2675000}},     // 最后一个 Tangerine 区块
		{2675000, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}},     // 第一个 Spurious 区块
		{4369999, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}},     // 最后一个 Spurious 区块
		{4370000, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}},     // 第一个 Byzantium 区块
		{7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}},     // 最后一个 Byzantium 区块
		{7280000, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}},     // 第一个和最后一个 Constantinople,第一个 Petersburg 区块
		{9068999, 0, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}},     // 最后一个 Petersburg 区块
		{9069000, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}},     // 第一个 Istanbul 和第一个 Muir Glacier 区块
		{9199999, 0, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}},     // 最后一个 Istanbul 和第一个 Muir Glacier 区块
		{9200000, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}},    // 第一个 Muir Glacier 区块
		{12243999, 0, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}},   // 最后一个 Muir Glacier 区块
		{12244000, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}},   // 第一个 Berlin 区块
		{12964999, 0, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}},   // 最后一个 Berlin 区块
		{12965000, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}},   // 第一个 London 区块
		{13772999, 0, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}},   // 最后一个 London 区块
		{13773000, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}},   // 第一个 Arrow Glacier 区块
		{15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}},   // 最后一个 Arrow Glacier 区块
		{15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 18000000}},   // 第一个 Gray Glacier 区块
		{18000000, 0, ID{Hash: checksumToBytes(0x4fb8a872), Next: 1668000000}}, // 第一个 Merge Start 区块
		{20000000, 0, ID{Hash: checksumToBytes(0x4fb8a872), Next: 1668000000}}, // 最后一个 Merge Start 区块
		{20000000, 1668000000, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}}, // 第一个 Shanghai 区块
		{20100000, 2669000000, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}}, // 未来的 Shanghai 区块
	},
}

这是一套测试,测试了主网节点可能处于的不同状态,以及可能需要验证并决定接受或拒绝的不同远程分叉标识符:

tests := []struct {
	head uint64
	id   ID
	err  error
}{
	/// 本地是主网 Withdrawals,远程宣布相同。没有宣布未来的分叉。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xc1fdf181), Next: 0}, nil},

	// 本地是主网 Withdrawals,远程宣布相同,也宣布了下一个分叉
	// 在区块/时间 0xffffffff,但不确定。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xc1fdf181), Next: math.MaxUint64}, nil},

	// 本地是主网,目前仅在 Byzantium 中(因此它知道 Petersburg & Withdrawals),远程也宣布
	// Byzantium,但它尚未意识到 Petersburg(例如,分叉之前未更新的节点)。
	// 在这种情况下,我们不知道 Petersburg 是否已经通过。
	{7279999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil},

	// 本地是主网,目前仅在 Byzantium 中(因此它知道 Petersburg & Withdrawals),远程也宣布
	// Byzantium,并且它也意识到 Petersburg(例如,分叉之前更新的节点)。我们
	// 不知道 Petersburg 是否已经通过(将通过)。
	{7279999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil},

	// 本地是主网,目前仅在 Byzantium 中(因此它知道 Petersburg & Withdrawals),远程也宣布
	// Byzantium,并且它也意识到一些随机分叉(例如,配置错误的 Petersburg)。由于
	// 两个节点都没有通过分叉,因此它们可能不匹配,但我们暂时仍然连接。
	{7279999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: math.MaxUint64}, nil},

	// 本地正好在主网 Withdrawals 上,远程宣布 Byzantium + 关于 Petersburg 的知识。远程
	// 只是不同步,接受。
	{20000000, 1668000000, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil},

	// 本地是主网 Withdrawals,远程宣布 Byzantium + 关于 Petersburg 的知识。远程
	// 只是不同步,接受。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil},

	// 本地是主网 Withdrawals,远程宣布 Spurious + 关于 Byzantium 的知识。远程
	// 肯定不同步。它可能需要或不需要 Petersburg 更新,我们尚不知道。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil},

	// 本地是主网 Byzantium & pre-withdrawals,远程宣布 Petersburg。本地不同步,接受。
	{7279999, 1667999999, ID{Hash: checksumToBytes(0x668db0af), Next: 0}, nil},

	// 本地是主网 Spurious,远程宣布 Byzantium,但没有意识到 Petersburg。本地
	// 不同步。本地也知道未来的分叉,但这还不确定。
	{4369999, 1667999999, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, nil},

	// 本地是主网 Withdrawals。远程宣布 Byzantium 但不知道进一步的分叉。
	// 远程需要软件更新。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 0}, ErrRemoteStale},

	// 本地是主网 Withdrawals,并且没有意识到更多的分叉。远程宣布 Petersburg +
	// 0xffffffff。本地需要软件更新,拒绝。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale},

	// 本地是主网 Withdrawals,并且已经意识到 Petersburg。远程宣布 Petersburg +
	// 0xffffffff。本地需要软件更新,拒绝。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0x5cddc0e1), Next: 0}, ErrLocalIncompatibleOrStale},

	// 本地是主网 Withdrawals,远程是 Rinkeby Petersburg。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},

	// 本地是主网 Withdrawals,遥遥领先。远程在一些未来的区块 88888888 处宣布 Gopherium(不存在的分叉)
	// 为了自己,但是对于本地来说是过去的区块。本地不兼容。
	//
	// 这种情况检测到具有多数哈希算力的未升级节点(典型的 Ropsten 混乱)。
	{88888888, 1668000001, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 88888888}, ErrRemoteStale},

	// 本地是主网 Withdrawals。远程在 Byzantium 中,但在 Petersburg 之前的区块 7279999 处宣布 Gopherium(不存在的
	// 分叉)。本地不兼容。
	{20000000, 1668000001, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrRemoteStale},

安全考虑

没有已知的安全风险

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Marius van der Wijden (@MariusVanDerWijden), "EIP-6122: 基于时间戳的 Forkid 检查," Ethereum Improvement Proposals, no. 6122, December 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6122.