写点什么

substrate 技术每周速览 20220411

作者:彭亚伦
  • 2022 年 6 月 27 日
  • 本文字数:2116 字

    阅读完需:约 7 分钟

ECDSA 公钥转换以太坊地址


本周 substrate 更新扩展了 ESDSA 签名相关功能, 添加了 ECDSAExt trait 以扩展 ECDSA 签名 , 目前其包含一个 to_eth_address 方法, 可以实现从 ESDSA 公钥到以太坊兼容地址的转换. 同时为跨链桥协议模块 beefy-mmr 以及合约模块 contract 添加相关调用方法和测试模块. #跨链 # #ink 合约 #


其主要是添加 frame/support/src/crypto/ecdsa.rs 文件, 代码如下:


```rust

pub trait ECDSAExt {	/// Returns Ethereum address calculated from this ECDSA public key.	fn to_eth_address(&self) -> Result<[u8; 20], ()>;}impl ECDSAExt for Public {	fn to_eth_address(&self) -> Result<[u8; 20], ()> {		use k256::{elliptic_curve::sec1::ToEncodedPoint, PublicKey};		PublicKey::from_sec1_bytes(self.as_slice()).map_err(drop).and_then(|pub_key| {			// uncompress the key			let uncompressed = pub_key.to_encoded_point(false);			// convert to ETH address			<[u8; 20]>::try_from(				sp_io::hashing::keccak_256(&uncompressed.as_bytes()[1..])[12..].as_ref(),			)			.map_err(drop)		})	}}
复制代码

```


代码比较简单, 主要是利用 k256 这个 crate 和 sp_io::hashing::keccak_256 将 ECDSA 地址解压缩后再转换成以太坊地址格式. 有了这个 to_eth_address, 最直接的收益就是 BEEFY 协议相关模块, 以及 contract 模块; 前者主要用来达成与以太坊的桥接(bridge), 而后者则需要扩展以兼容以太坊的合约.扩展阅读: BEEFY 和 BEEFY-MMR


波卡生态最重要的特点就是优异跨链能力, 接入波卡的平行链网络如 Moonbean, Acala 等都是基于 susbtrate 开发, 因此可以通过接入中继链的方式进行跨链通信; 但是对于那些非 substrate 开发的链, 比如比特币/以太坊 这些传统区块链, 该如何接入波卡, 并与波卡生态进行跨链通信呢?


BEEFY 协议就是为此而生.


BEEFY 共识也称 BEEFY 协议,全称是 Bridge Efficiency Enabling Finality Yielder,是一个辅助共识协议, 需要与其它共识如 GRANDPA 一起运行; 其主要用于支持与非 Substrate 区块链的更高效桥接; 如以太坊或者是比特币网络与 Substrate 网络之间的跨链桥接.


BEEFY 的应用上通常需要跟某种数据聚合结构(如 Merkle 树)结合使用, 主要原因是 substrate 的最终块确认是基于 GRANDPA 协议, 该协议的特点是一次性确认一条子链, 而不是单个区块; 但是比特币, 以太坊等传统区块链则是确认一个个区块; 因此要在 substrate 链和以太坊中间做桥接, 需要有一种新的聚合数据结构, 来兼容两种共识体系的不同.


MMR (Merkle Mountain Range)恰好就是这样一种结构, 而且 MMR 是更高效更适合区块链网络的 Merkle 树结构; 因此 substrate 贴心地提供了 BEEFY 和 BEEFY-MMR 相关模块.


更多关于 BEEFY 协议和 MMR, 请关注本专栏后续的专文.优化最终块确认及通知


本周另一个比较重点的优化, 在于针对近期出现的收集者(collator) 卡顿的情况, 优化了最终块确认的过程.


substrate 链的最终块确认共识引擎是 GRANDPA, 由于 GRANDPA 的确认方式比较独特, 是通过一次性确认多个区块形成的子链的方式来完成, 如图:


图中, 每个验证者(validtor)都会对自己认为最合适的子链投票, 然后系统选出 2/3 以上的验证者投票的最长子链作为通过, 图中为 A->B-C 组成的最长的子链.


由图中可以看出, 最佳区块即高度最大的块(图中为 F1), 与最后确认的区块(图中为 C)直接有一定的空隙, 这个空隙中有些块会在将来被确认, 而有些则属于过期头(state head), 需要被丢弃的.


而收集者的卡顿, 经过检查发现, 主要是由于确认区块(finalized block)和最佳区块(best block) 之间间隔高度过大, 导致收集者(collator)需要花很多时间去检查和排除那些过期区块头信息(stale heads);


鉴于原有的 leaves 模块已经提供方法可以获知过期头列表, 但由于返回的是所有不可达的过期头; 因此本此 PR 在其基础上做了小修改, 添加 displaced_by_finalize_height 方法, 使得只有那些高度低于确认块的 stale heads 被移除而不是所有不可达区块.


pub fn displaced_by_finalize_height(&self, number: N) -> FinalizationDisplaced<H, N> {		let boundary = if number == N::zero() {			return FinalizationDisplaced { leaves: BTreeMap::new() }		} else {			number - N::one()		};		let below_boundary = self.storage.range(&Reverse(boundary)..);		FinalizationDisplaced {			leaves: below_boundary.map(|(k, v)| (k.clone(), v.clone())).collect(),		}	}
复制代码

其调用方式如下代码所示:


fn displaced_leaves_after_finalizing(		&self,		block_number: NumberFor<Block>,	) -> sp_blockchain::Result<Vec<Block::Hash>> {		Ok(self			.storage			.read()			.leaves			.displaced_by_finalize_height(block_number)			.leaves()			.cloned()			.collect::<Vec<_>>())	}
复制代码


可以看出, 通过 leaves 模块获得过期头列表, 然后使用 displaced_by_finalize_height 方法去掉部分高度较低的块, 这样大大优化了最终块确认的时间.


那么, 那些高度高于最终块的那些呢? 这些将会在后续轮里面被丢弃. 所以不用担心旧数据在链上留存过多的问题.


关于 GRANDPA 的详细解析, 还请关注后续专文.


本文已开启永久链接, 属于 SEP Creation 原创组文章, 未经许可, 请勿转载.

发布于: 刚刚阅读数: 3
用户头像

彭亚伦

关注

还未添加个人签名 2021.01.25 加入

还未添加个人简介

评论

发布
暂无评论
substrate 技术每周速览 20220411_rust_彭亚伦_InfoQ写作社区