以太坊2.0规范:Casper和分片

当前位置:首页 > 币圈百科 > 以太坊2.0规范:Casper和分片

以太坊2.0规范:Casper和分片

2022-11-18币圈百科240

译者按:以太坊2.0是指以Casper、分片等技术为核心,旨在大幅扩容以太坊平台的方案,而其中就涉及到一个称为“beacon链”的核心PoS链概念,而它与以太坊PoW主链之间会是什么样的关系呢?

据悉,这一概念最初是由以太坊创始人Vitalik Buterin在今年初时提出的,目前在JustinDrake、djrtwo以及mkalinin三人的帮助下继续完善。截至发稿时,以太坊2.0的规范完成度接近了60%。

注:文章中的内容反映的是以太坊研究人员和实施者正在进行当中的工作,但其需要不断地完善。

eth2.0

以下为规范译文:

在以太坊2.0当中,有一个被称为“beacon链”的核心系统链,这个beacon链负责存储和管理当前活跃PoS验证者的集合。而最初成为验证者的唯一机制,是在现有的以太坊1.0 PoW主链发送一笔包含32 ETH 的交易至一个注册合约。当你这样做时,一旦beacon?PoS链处理了该区块,你就会进入队列,并最终成为一名活跃验证者。当然,你也可以自愿注销验证者身份,或者因为自己的不当行为而被强行注销身份。

beacon链的主要负载来源是证明(attestation)。这些证明会同时确认一个分片区块,以及相对应的beacon链区块。对于相同的分片区块,足够数量的证明就创建了一个“交联”(crosslink)。“交联”用于“确认”分片链的片段进入了?beacon链,并且这也是不同分片之间互相通信的主要方式。

注:项目的python代码库地址为:https://github.com/ethereum/beacon_chain

一、术语

1、验证者(Validator):参与Casper/分片 共识系统的参与者。你可以通过将32 ETH存入这个Casper机制而成为一名验证者。

2、活跃验证者集(Active validator set):指那些正在参与的验证者,Casper机制希望产生并证明区块、“交叉链接”以及其它共识对象;

3、委员会(Committee):活跃验证者集的一个(伪)随机抽样子集。当一个委员会被集体提及时,正如“这个委员会证明了X”一样,这就意味着,该委员会的一些子集包含了足够的验证者,以致协议承认它代表着该委员会。

4、提出者(Proposer):创建区块的验证者;

5、证明者(Attester):它是委员会的验证成员,其需要在一个区块上进行签名操作;

6、Beacon链(Beacon chain):分片系统的基础,即核心PoS链;

7、分片链(Shard chain):处理用户交易的链之一,用于存储账户数据;

8、交联(Crosslink):来自委员会的一组签名,其可证明一个分片链中的区块,它们可以被包含在beacon链当中。交联是beacon链“了解”分片链更新状态的主要手段;

9、Slot:SLOT_DURATION周期时间,在此期间,一个提出者有能力创建一个区块,而某些证明者能够进行证明工作;

10、朝代变迁(Dynasty transition):验证者集的变化;

11、朝代(Dynasty):自创世以来,在给定链中发生的朝代变迁的数量;

12、周期(Cycle):在这个时期内,所有验证者都有一次投票的机会(除非一个朝代的转变发生在内部)

13、完成区块及合理区块(Finalized,?justified),见Casper FFG定稿:https://arxiv.org/abs/1710.09437

14、取款周期(Withdrawal period):验证者退出与验证者余额可取款之间的slot数量;

15、创始时间(Genesis time):beacon链在slot 为0时的创始区块UNIX时间;

二、常量

P1

验证者状态码:

P1

特殊记录类型:

p2

验证者集增量标志:

p3

三、PoW主链注册合约

以太坊2.0的初始部署阶段,是不需要对PoW链进行共识更改的。相反的是,我们会向PoW链添加一个注册合约,以存入ETH。这个合约有一个registration注册函数,它采用了pubkey, withdrawal_shard, withdrawal_address, randao_commitment 这些参数,正如下面的ValidatorRecord中所定义的。

这个注册合约会通过beacon链发出一个带有各种参数的日志。它不会进行验证,而是把注册逻辑发送给beacon链。需要注意的是,注册合约不会去验证占有证明(基于BLS12-381曲线)。

四、Beacon 链数据结构

Beacon链是PoS系统的“主链”,beacon链的主要职责是:

存储并维护活跃、列队等待以及退出验证者的集合;处理交联(见上文);处理逐块一致性,以及最终小工具(finality gadget);

4、1 ? Beacon 链区块

一个BeaconBlock具有以下字段:

{# Slot number'slot': 'int64',# Proposer RANDAO reveal'randao_reveal': 'hash32',# Recent PoW chain reference (block hash)'pow_chain_reference': 'hash32',# Skip list of ancestor block hashes (i'th item is 2**i'th ancestor (or zero) for i = 0, ..., 31)'ancestor_hashes': ['hash32'],# Active state root'active_state_root': 'hash32',# Crystallized state root'crystallized_state_root': 'hash32',# Attestations'attestations': [AttestationRecord],# Specials (e.g. logouts, penalties)'specials': [SpecialRecord]}

一个AttestationRecord具有以下字段:

{ # Slot number 'slot': 'int64', # Shard number 'shard': 'int16', # Block hashes not part of the current chain, oldest to newest 'oblique_parent_hashes': ['hash32'], # Shard block hash being attested to 'shard_block_hash': 'hash32', # Attester participation bitfield (1 bit per attester) 'attester_bitfield': 'bytes', # Slot of last justified block 'justified_slot': 'int64', # Hash of last justified block 'justified_block_hash': 'hash32', # BLS aggregate signature 'aggregate_sig': 蓑衣网小编2022 ['int256']}

一个AttestationSignedData具有以下字段:

{ # Chain version 'version': 'int64', # Slot number 'slot': 'int64', # Shard number 'shard': 'int16', # 31 parent hashes 'parent_hashes': ['hash32'], # Shard block hash 'shard_block_hash': 'hash32', # Slot of last justified block referenced in the attestation 'justified_slot': 'int64'}

一个SpecialRecord具有以下字段:

{ # Kind 'kind': 'int8', # Data 'data': ['bytes']}

4、2 ?Beacon链状态

beacon链状态分为了活跃状态(active state )和结晶状态(crystallized state)这两个部分;

下面是活跃状态ActiveState具有的字段:

{ # Attestations not yet processed 'pending_attestations': [AttestationRecord], # Specials not yet been processed 'pending_specials': [SpecialRecord] # Most recent 2 * CYCLE_LENGTH block hashes, older to newer 'recent_block_hashes'蓑衣网小编2022: ['hash32'], # RANDAO state 'randao_mix': 'hash32'}

下面是结晶状态CrystallizedState具有的字段:

{ # Dynasty number 'dynasty': 'int64', # Dynasty seed (from randomness beacon) 'dynasty_seed': 'hash32', # Dynasty start 'dynasty_start_slot': 'int64', # List of validators 'validators': [ValidatorRecord], # Most recent crosslink for each shard 'crosslinks': [CrosslinkRecord], # Last crystallized state recalculation 'last_state_recalculation_slot': 'int64', # Last finalized slot 'last_finalized_slot': 'int64', # Last justified slot 'last_justified_slot': 'int64', # Number of consecutive justified slots 'justified_streak': 'int64', # Committee members and their assigned shard, per slot 'shard_and_committee_for_slots': [[ShardAndCommittee]], # Total deposits penalized in the given withdrawal period 'deposits_penalized_in_period': ['int32'], # Hash chain of validator set changes (for light clients to easily track deltas) 'validator_set_delta_hash_chain': 'hash32' # Parameters relevant to hard forks / versioning. # Should be updated only by hard forks. 'pre_fork_version': 'int32', 'post_fork_version': 'int32', 'fork_slot_number': 'int64',}

一个ValidatorRecord具有以下字段:

{ # BLS public key 'pubkey': 'int256', # Withdrawal shard number 'withdrawal_shard': 'int16', # Withdrawal address 'withdrawal_address': 'address', # RANDAO commitment 'randao_commitment': 'hash32', # Balance 'balance': 'int64', # Status code 'status': 'int8', # Slot when validator exited (or 0) 'exit_slot': 'int64'}

一个CrosslinkRecord具有以下字段:

{ # Dynasty number 'dynasty': 'int64', # Slot number 'slot': 'int64', # Beacon chain block hash 'shard_block_hash': 'hash32'}

一个ShardAndCommittee 对象具有以下字段:

fields = {# The shard ID'shard_id': 'int16',# Validator indices'committee': ['int24']}

4、3 ?Beacon链的处理

处理beacon链,在很多方面与处理PoW链有着相似之处。例如客户端下载、处理区块,维护当前的“权威链”,并终止于当前“头部”区块。然而,由于beacon链与现有PoW链之间的关系,并且beacon链是一种PoS链,两者还是有很大不同的。

对于beacon链上节点所处理的区块,必须要满足3个条件:

由ancestor_hashes[0]指向的父对象已被处理及接受。由pow_chain_ref所指向的PoW链区块已被处理及接受;节点的本地时间大于或等于由GENESIS_TIME + slot_number * SLOT_DURATION 计算得出的最小时间戳;

如果不满足这三个条件,则客户端应延迟处理区块,直到满足这几个条件为止。

在区块生产方面,由于PoS机制的原因,beacon链显然与PoW链存在着显著差异。客户端应检查权威链应该在什么时候创建区块,并查找它的slot(注:类似区块高度)数;当slot到达时,它会根据要求提出或证明一个区块;

4、4。Beacon链分叉选择规则

beacon链使用了Casper FFG分叉选择规则,即“支持包含最高合理检查点(highest-epoch justified checkpoint)的区块链”。为了从相同合理检查点派生而出的两条链之间进行选择,区块链会使用即时消息驱动GHOST(IMD GHOST)方案来选择出权威链 。

具体描述参见:https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760

相关模拟实现代码库,请参见:

https://github.com/ethereum/research/blob/master/clock_disparity/ghost_node.py

下图展示了系统工作的一个例子(绿色是完成区块,黄色代表合理区块,灰色代表证明):

p5

五、Beacon链状态转移函数

我们现在定义一下状态转移函数。从高层次讲,状态转换由两个部分组成:

结晶状态重计算,这只有当block.slot_number >= last_state_recalc + CYCLE_LENGTH 时才会发生,而它会影响CrystallizedState以及ActiveState;每区块处理(per-block processing),它发生在每个区块,并且其只影响ActiveState;

结晶状态重计算通常集中于对验证者集的更改,包括调整余额,添加和删除验证者,以及处理交联( crosslink)和设置FFG检查点,而每区块处理通常集中于验证聚合签名并保存与ActiveState区块内活动有关的临时记录。

5、1 ?辅助函数(Helper functions)

我们首先定义一些辅助算法。首先是,选择活跃验证者的函数:

def get_active_validator_indices(validators): return [i for i, v in enumerate(validators) if v.status == ACTIVE]

接下来是洗牌这个列表的函数:

def shuffle(lst, seed): # entropy is consumed in 3 byte chunks # rand_max is defined to remove the modulo bias from this entropy source rand_max = 2**24 assert len(lst) <= rand_max o = [x for x in lst] source = seed i = 0 while i < len(lst): source = hash(source) for pos in range(0, 30, 3): m = int.from_bytes(source[pos:pos+3], 'big') remaining = len(lst) - i if remaining == 0: break rand_max = rand_max - rand_max % remaining if m < rand_max: replacement_pos = (m % remaining) + i o[i], o[replacement_pos] = o[replacement_pos], o[i] i += 1 return o

下面是一个将列表拆分成N块的函数:

def split(lst, N): return [lst[len(lst)*i//N: len(lst)*(i+1)//N] for i in range(N)]

接下来,是我们的组合辅助函数:

def get_new_shuffling(seed, validators, crosslinking_start_shard): active_validators = get_active_validator_indices(validators) if len(active_validators) >= CYCLE_LENGTH * MIN_COMMITTEE_SIZE: committees_per_slot = min(len(active_validators) // CYCLE_LENGTH // (MIN_COMMITTEE_SIZE * 2) + 1, SHARD_COUNT // CYCLE_LENGTH) slots_per_committee = 1 else: committees_per_slot = 1 slots_per_committee = 1 while len(active_validators) * slots_per_committee < CYCLE_LENGTH * MIN_COMMITTEE_SIZE \ and slots_per_committee < CYCLE_LENGTH: slots_per_committee *= 2 o = [] for i, slot_indices in enumerate(split(shuffle(active_validators, seed), CYCLE_LENGTH)): shard_indices = split(slot_indices, committees_per_slot) shard_start = crosslinking_start_shard + \ i * committees_per_slot // slots_per_committee o.append([ShardAndCommittee( shard = (shard_start + j) % SHARD_COUNT, committee = indices ) for j, indices in enumerate(shard_indices)]) return o

下面是一个关于正在发生的图解:p5

我们还定义了两个函数:

def get_shards_and_committees_for_slot(crystallized_state, slot): earliest_slot_in_array = crystallized_state.last_state_recalculation_slot - CYCLE_LENGTH assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return crystallized_state.shard_and_committee_for_slots[slot - earliest_slot_in_array]def get_block_hash(active_state, curblock, slot): earliest_slot_in_array = curblock.slot - CYCLE_LENGTH * 2 assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return active_state.recent_block_hashes[slot - earliest_slot_in_array]

其中get_block_hash(_, _, s)应该始终以slot s返回链中的区块,而get_shards_and_committees_for_slot(_, s)则不应该改变,除非朝代(dynasty)发生改变。

我们还定义了一个函数,为验证者哈希链“添加了一个link”,这在添加或移除一个验证者时使用:

def add_validator_set_change_record(crystallized_state, index, pubkey, flag): crystallized_state.validator_set_delta_hash_chain = \ hash(crystallized_state.validator_set_delta_hash_chain + bytes1(flag) + bytes3(index) + bytes32(pubkey))

最后,我们抽象地将用于奖励/惩罚计算的int_sqrt(n)定义为最大整数k,使得k**2<=n:

def int_sqrt(n): x = n y = (x + 1) // 2 while y < x: x = y y = (x + n // x) // 2 return x

5、2 关于启动(On startup)

运行以下代码:

def on_startup(initial_validator_entries): # Induct validators validators = [] for pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, \ randao_commitment in initial_validator_entries: add_validator(validators, pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, randao_commitment) # Setup crystallized state cs = CrystallizedState() x = get_new_shuffling(bytes([0] * 32), validators, 0) cs.shard_and_committee_for_slots = x + x cs.dynasty = 1 cs.crosslinks = [CrosslinkRecord(dynasty=0, slot=0, hash=bytes([0] * 32)) for i in range(SHARD_COUNT)] # Setup active state as = ActiveState() as.recent_block_hashes = [bytes([0] * 32) for _ in range(CYCLE_LENGTH * 2)]

CrystallizedState()和ActiveState()构造函数应根据上下文将所有值初始化为零字节、空值或空数组。add_validator程序定义如下:

def add_validator(validators, pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, randao_commitment): # if following assert fails, validator induction failed # move on to next validator registration log assert BLSVerify(pub=pubkey, msg=hash(pubkey), sig=proof_of_possession) rec = ValidatorRecord( pubkey=pubkey, withdrawal_shard=withdrawal_shard, withdrawal_address=withdrawal_address, randao_commitment=randao_commitment, balance=DEPOSIT_SIZE * GWEI_PER_ETH, # in Gwei status=PENDING_ACTIVATION, exit_slot=0 ) index = min_empty_validator(validators) if index is None: validators.append(rec) return len(validators) - 1 else: validators[index] = rec return index

5、3 处理每个区块(Per-block processing)

这个程序应该在每个区块上执行。

首先,将recent_block_hashes设置为以下输出,其中parent_hash是前一个区块的哈希:

def get_new_recent_block_hashes(old_block_hashes, parent_slot, current_slot, parent_hash): d = current_slot - parent_slot return old_block_hashes[d:] + [parent_hash] * min(d, len(old_block_hashes))

get_block_hash的输出不应该改变,除非它不再抛出 current_slot - 1,而是抛出current_slot - CYCLE_LENGTH * 2 - 1

此外,使用以下算法检查区块的ancestor_hashes数组是否被正确更新:

def update_ancestor_hashes(parent_ancestor_hashes, parent_slot_number, parent_hash): new_ancestor_hashes = copy.copy(parent_ancestor_hashes) for i in range(32): if parent_slot_number % 2**i == 0: new_ancestor_hashes[i] = parent_hash return new_ancestor_hashes

区块可以有0个或多个AttestationRecord对象,其中每个AttestationRecord对象具有以下字段:

fields = { # Slot number 'slot': 'int64', # Shard ID 'shard_id': 'int16', # List of block hashes that this signature is signing over that # are NOT part of the current chain, in order of oldest to newest 'oblique_parent_hashes': ['hash32'], # Block hash in the shard that we are attesting to 'shard_block_hash': 'hash32', # Who is participating 'attester_bitfield': 'bytes', # The actual signature 'aggregate_sig': ['int256']}

对于这些证明中的每一个:

验证slot <= parent.slot 以及slot >= max(parent.slot - CYCLE_LENGTH + 1, 0);验证给定链中的justified_slot以及justified_block_hash等于或早于结晶状态的last_justified_slot计算parent_hashes = [get_block_hash(crystallized_state, active_state, block, slot - CYCLE_LENGTH + i) for i in range(CYCLE_LENGTH - len(oblique_parent_hashes))] + oblique_parent_hashes让 attestation_indices成为 get_indices_for_slot(crystallized_state, active_state, slot)

,选择 x 那么 attestation_indices.shard_id 就等于 所提供的 shard值,以找到创建证明记录的验证者集;验证len(attester_bitfield) == ceil_div8(len(attestation_indices)), 其中ceil_div8 = (x + 7) // 8. 验证位len(attestation_indices)…. 以及更高的, 如果出现的 (例如. len(attestation_indices)结果不是8的倍数), 则全为0让 version = pre_fork_version if slot < fork_slot_number else post_fork_version.使用生成的公钥组以及序列化形式的AttestationSignedData(version, slot, shard, parent_hashes, shard_block_hash, justified_slot) 验证 aggregate_sig;

5、4 状态重计算

当slot - last_state_recalc >= CYCLE_LENGTH时进行重复过程;

对于 last_state_recalc - CYCLE_LENGTH ... last_state_recalc - 1范围中所有的slot s;

让total_balance成为活跃验证者的总余额;让total_balance_attesting成为beacon链区块在slot s 时验证者的总余额确定这些验证者的总余额,如果该值乘以3等于或超过所有活跃验证者乘以2的总余额(3 * total_balance_attesting_at_s >= 2 * total_balance),则设置 last_justified_slot = max(last_justified_slot, s) 以及 justified_streak += 1。否则,则设置justified_streak = 0;如果justified_streak >= CYCLE_LENGTH + 1,则设置last_finalized_slot = max(last_finalized_slot, s – CYCLE_LENGTH – 1);删除所有比 last_state_recalc slot更早的证明记录;

还有:

1、设置last_state_recalc += CYCLE_LENGTH;2、设置indices_for_slots[:CYCLE_LENGTH] = indices_for_slots[CYCLE_LENGTH:];

对于所有(shard_id, shard_block_hash) 元组,计算为该分片区块哈希投票的验证者总存款大小。如果该值乘以3等于或超过委员会中所有验证者的总余额乘以2,并且当前朝代(dynasty)超过crosslink_records[shard_id].dynasty,则设置crosslink_records[shard_id] = CrosslinkRecord(dynasty=current_dynasty, hash=shard_block_hash);

待办事项:

FFG参与奖励;委员会参与奖励;

六、朝代变迁(Dynasty transition)

如果满足下列所有标准,则在状态重新计算之后发生了朝代变迁:

block.slot - crystallized_state.dynasty_start_slot >= MIN_DYNASTY_LENGTHlast_finalized_slot > dynasty_start_slot对于shard_and_committee_for_slots中的每一个shard分片数,crosslinks[shard].slot > dynasty_start_slot

然后,运行下面的算法来更新验证者集合:

def change_validators(validators): # The active validator set active_validators = get_active_validator_indices(validators, dynasty) # The total balance of active validators total_balance = sum([v.balance for i, v in enumerate(validators) if i in active_validators]) # The maximum total wei that can deposit+withdraw max_allowable_change = max( 2 * DEPOSIT_SIZE GWEI_PER_ETH, total_balance // MAX_VALIDATOR_CHURN_QUOTIENT ) # Go through the list start to end depositing+withdrawing as many as possible total_changed = 0 for i in range(len(validators)): if validators[i].status == PENDING_ACTIVATION: validators[i].status = ACTIVE total_changed += DEPOSIT_SIZE add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, ENTRY) if validators[i].status == PENDING_EXIT: validators[i].status = PENDING_WITHDRAW validators[i].exit_slot = current_slot total_changed += validators[i].balance add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, EXIT) if total_changed >= max_allowable_change: break # Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods period_index = current_slot // WITHDRAWAL_PERIOD total_penalties = ( (crystallized_state.deposits_penalized_in_period[period_index]) + (crystallized_state.deposits_penalized_in_period[period_index - 1] if period_index >= 1 else 0) + (crystallized_state.deposits_penalized_in_period[period_index - 2] if period_index >= 2 else 0) ) # Separate loop to withdraw validators that have been logged out for long enough, and # calculate their penalties if they were slashed for i in range(len(validators)): if validators[i].status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= validators[i].exit_slot + WITHDRAWAL_PERIOD: if validators[i].status == PENALIZED: validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_balance) // total_balance validators[i].status = WITHDRAWN withdraw_amount = validators[i].balance ... # STUB: withdraw to shard chain

最后:

设置 last_dynasty_start_slot = crystallized_state.last_state_recalculation_slot设置 crystallized_state.dynasty += 1设置 next_start_shard = (shard_and_committee_for_slots[-1][-1].shard + 1) % SHARD_COUNT设置 shard_and_committee_for_slots[CYCLE_LENGTH:] = get_new_shuffling(active_state.randao_mix, validators, next_start_shard)

七、尚未完成的工作

注:这个项目的完成度大约为60%,所缺少的主要部分为:

具体怎么样构造 crystallized_state_root以及active_state_root,包括用于轻客户端的默克尔化证明;关于pow_chain_reference可接受值的具体规则;关于分片链区块、提出者等具体规则;关于强制撤销登记的具体规则;关于(全局时钟、网络延迟、验证者诚实、验证者活跃度等)问题的各种假设;关于监护证明的逻辑,包括削减条件;添加BLS12-381曲线的附录;添加gossip网络以及链外签名聚合逻辑的附录;添加一个词汇表(在单独的词汇表中),以全面而准确地定义所有术语;进行同行评审、安全审核和形式验证;

可能的修订/增补 工作

用LMD替换IMD分叉选择规则;将crystallized_state_root 以及 active_state_root 默克尔化成一个单独的根;用一个对STARK友好的哈希函数替换掉Blake;去掉朝代(dynasties)的概念;将slot直接换成8秒;允许延迟聚合签名的包含;引入一种RANDAO削减条件;对于占有证明,使用一种单独的哈希函数;修改ShardAndCommittee数据结构;为历史beacon链区块添加一个双批(double-batched)的默克尔累加器;允许存款大于32ETH,并设置存款上限;对于存款低于32ETH(或其它阈值)的情况,设置一个罚金;为寄存器添加一个SpecialRecord;重写文档可读性;清楚地记录各种边缘情况,例如委员会的大小;

附录

附录A -哈希函数

我们的目标是在推出beacon链时,拥有一个对STARK友好的哈希函数hash(x)。这个哈希函数的标准化过程,是由STARKware主导的,他们将制定详细的报告说明。我们会使用BLAKE2b-512作为占位符。明确地说,我们设置了hash(x) := BLAKE2b-512(x)[0:32] ,其中BLAKE2b-512算法是在RFC 7693中定义的。[x]
以太坊2.0规范:Casper和分片 | 分享给朋友: