All pages
Powered by GitBook
1 of 11

Peggy

摘要

peggy模块使Injective链能够支持一个可信的、链上双向ERC-20代币桥接到以太坊。在该系统中,以太坊上的ERC-20代币持有者可以将他们的ERC-20代币转换为Injective链上的Cosmos原生币,反之亦然。 这个去中心化的桥接由Injective链的验证者进行安全保障和操作。

目录

  1. 定义

  2. 工作流

  3. 状态

  4. 消息

  5. 处罚

  6. End-Block

  7. 事件

  8. 参数

组件

  1. Peggy 智能合约(Ethereum)

  2. Peggy 模块(Injective Chain)

  3. Peggo(链下中继器,又称orchestrator)

    1. Oracle(监测Peggy合约事件并向Peggy模块发送声明)

    2. EthSigner(对Valset和批量确认进行签名并发送至Peggy模块)

    3. Batch Requester(向Peggy模块发送批量代币提取请求)

    4. Valset Relayer(向Peggy合约提交验证人集合更新)

    5. Batch Relayer(向Peggy合约提交批量代币提取)

此外,Injective Chain验证人除了运行injectived节点以签署区块外,还必须运行peggo orchestrator,以在Ethereum上的Peggy智能合约与Injective Chain上的Peggy模块之间中继数据。

Peggo 功能

  1. 在Ethereum上维护Injective Chain验证人集合的最新检查点

  2. 从Ethereum向Injective Chain转移ERC-20代币

  3. 从Injective Chain向Ethereum转移锚定代币

定义

本文档旨在从技术角度提供关于Peggy(Injective的以太坊桥接)的概述,并深入探讨其操作逻辑。Peggy是基于Injective构建的自定义Cosmos SDK模块的名称,也是以太坊合约(Peggy.sol)的名称,它们构成了桥接的两端。通过一个名为Peggo的中介过程连接,用户可以安全地在网络之间转移代币资产。 如有改进建议,请提交GitHub issue。

关键定义

用词很重要,我们追求术语的清晰,以便在思考和沟通中保持清晰。为了帮助更好地理解,以下是一些关键定义:

  • Operator - 这是控制和操作验证者和协调者进程的个人(或团队)。

  • Validator - 这是Injective链的验证节点(例如:injectived进程)。

  • Validator Set - Injective链的(活动)验证者集(Valset),以及根据其股份权重确定的投票权。每个验证者都与一个以太坊地址关联,以便在以太坊网络上进行表示。

  • Orchestrator (Peggo) - 一个链下进程(peggo),在Injective和以太坊之间充当中介角色。协调者负责保持桥接在线,并需要活动的端点以完全同步Injective(以太坊)节点。

  • Peggy module - Peggy合约的对应Cosmos模块。除了提供资产桥接服务外,它还会随着时间推移自动反映在活动的验证者集上。更新随后通过Peggo应用到以太坊。

  • Peggy Contract - 以太坊合约,持有所有ERC-20代币。它还使用委托密钥和规范化的权重,维护Injective链验证者集的压缩检查点表示。

  • Delegate Keys - 当操作员首次设置协调器时,他们会在Injective上注册其验证者地址与以太坊地址。相应的密钥用于签署消息,并在以太坊上代表该验证者。可选地,还可以提供一个代理的Injective链账户密钥,以代表验证者签署Injective消息(例如,声明)。

  • Peggy Tx pool (withdrawals) - 当用户希望将其资产从Injective转移到以太坊时,他们的个人交易会与其他相同资产的交易一起被放入池中。

  • Peggy Batch pool - 汇聚的提款会被协调者批量处理,进行签名并最终转发到以太坊。这些批次会保存在这个池中。

  • Claim - 一个由协调者签署的证明,表明某个事件发生在Peggy合约中。

  • Attestation - 来自Peggy合约的某个事件随机数的声明的集合。在大多数协调者对一个声明进行确认后,事件将在Injective上被承认并执行。

  • Majority - Injective网络的多数,2/3 + 1的验证者。

  • Deposit - 从以太坊到Injective发起的资产转移。

  • Withdrawal - 从Injective到以太坊发起的资产转移(在Peggy Tx池中)。

  • Batch - 包含相同代币类型的提款批次(在Peggy Batch池中)。

工作流

概念概述

回顾一下,每个Operator负责维护两个安全进程:

  • 一个完全同步的Injective链验证节点(injectived进程)

  • 与两个网络交互的协调器服务(peggo orchestrator进程)。隐式地,还需要一个完全同步的以太坊节点的RPC端点(请参阅peggo .env示例)

这两个实体共同完成三件事:

  1. 将代币资产从以太坊转移到Injective

  2. 将代币资产从Injective转移到以太坊

  3. 保持Peggy.sol合约与Injective上的活动验证者集同步

即使不成为验证者,也可以运行peggo。当配置为使用与验证者地址无关的地址时,peggo会自动以“中继者模式”运行。在这种模式下,只有两件事可以发生:

  • 可以在Injective上创建新的代币批次

  • 已确认的验证者集/批次可以被转发到以太坊

资产类型

以太坊原生资产

任何来自以太坊的资产,只要实现了ERC-20标准,都可以通过调用sendToInjective函数在Peggy.sol合约中从以太坊转移到Injective,该函数将代币从发送者的余额转移到Peggy合约。

所有Operators都运行他们的peggo进程,这些进程提交MsgDepositClaim消息,描述它们观察到的存款。一旦超过66%的所有投票权提交了该特定存款的声明,代表代币就会被铸造并发行到发送者请求的Injective链地址。

这些代表代币的面额前缀为peggy,后接ERC-20代币的十六进制地址,例如peggy0xdac17f958d2ee523a2206206994597c13d831ec7。

原生 Cosmos SDK 资产

来自Cosmos SDK链的本地资产(例如ATOM)必须首先在以太坊上表示,才能进行桥接。为此,Peggy合约允许任何人通过调用deployERC20函数来创建一个表示Cosmos资产的新的ERC-20代币。

此端点没有权限限制,因此由验证者和Peggy桥的用户来声明任何给定的ERC-20代币作为某个资产的表示。

当以太坊上的用户调用deployERC20时,他们会传递描述所需资产的参数。Peggy.sol使用ERC-20工厂部署实际的ERC-20合约,并将新代币的全部余额的所有权赋予Peggy合约本身,然后触发ERC20DeployedEvent事件。

peggo协调者会观察到这个事件,并决定Cosmos资产是否已被准确表示(正确的小数位,正确的名称,没有预先存在的表示)。如果是这样,ERC-20合约地址将被采用并存储为该Cosmos资产在以太坊上的最终表示。

协调器(Peggo)子进程

peggo协调器进程由四个子进程组成,这些子进程在精确的时间间隔内并发运行(循环)。它们分别是:

  1. Signer:使用操作员的以太坊密钥签署新的验证者集更新和代币批次,并通过消息提交。

  2. Oracle:观察以太坊事件并将其作为声明发送到Injective。

  3. Relayer:将已确认的验证者集更新和代币批次提交到以太坊上的Peggy合约。

  4. Batch Creator:观察Injective上的(新)提款,并根据其类型和配置的PEGGO_MIN_BATCH_FEE_USD值决定哪些提款需要打包。

Batch Creator

Batch Creator 的目的是仅在 Injective 端创建代币批次。相关的 Peggy 模块 RPC 没有权限限制,因此任何人都可以创建批次。 当用户想要从 Injective 提取资产到以太坊时,他们会发送一条特殊消息到 Injective(MsgSendToEth),该消息将他们的提取请求添加到 Peggy Tx Pool 中。Batch Creator 会持续查询池中的提取请求(按代币类型),并在潜在批次满足配置的 PEGGO_MIN_BATCH_FEE_USD 值时(参见 .env 示例),向 Injective 发出 MsgRequestBatch。 在接收端,所有与请求中的代币类型匹配的池中提取请求将从 Outgoing Tx Pool 中移动,作为一个单独的批次放入 Outgoing Batch Pool 中。

Signer

Signer 的职责是提供确认,证明一个操作员(Orchestrator)参与了桥接活动。如果未能提供这些确认,将导致该操作员的验证节点遭受惩罚。换句话说,这个过程必须始终运行在验证节点上。 任何在 Injective->Ethereum 流水线中移动的负载(如验证器集更新/代币批次)都需要验证器签名才能成功转发到以太坊。某些在 Peggy 合约上的调用接受一组签名,并与合约中的验证器集进行比对。Orchestrators 使用他们的委托以太坊地址来生成这些签名:这是操作员在初始设置时决定的以太坊地址(SetOrchestratorAddress)。这个地址代表了该验证器在以太坊区块链上的身份,并将作为多签名成员添加,拥有尽可能接近 Injective 链投票权的加权投票权。 每当 Signer 发现 Peggy 模块中存在未确认的验证器集更新(代币批次)时,它会发出 MsgConfirmValset(MsgConfirmBatch)作为证明,证明该操作验证器在桥接活动中是活跃的。

Oracle

监控以太坊网络中涉及 Peggy 合约的新事件。 每个由合约触发的事件都有一个唯一的事件 nonce。这个 nonce 值对于协调 Orchestrators 正确观察合约活动至关重要,并确保 Injective 通过 Claims 进行确认。多个相同 nonce 的 claims 组成一个 Attestation,当大多数(2/3)的 Orchestrators 观察到某个事件时,其特定的逻辑会在 Injective 上执行。 如果 2/3 的验证器无法就单个 Attestation 达成一致,oracle 将被暂停。这意味着在一些验证器改变他们的投票之前,不会有新的事件从以太坊转发过来。对此没有 slashing 条件,详细理由在 slashing 规范中列出。 Peggy.sol 触发了 4 种类型的事件:

  • TransactionBatchExecutedEvent - 表示一个代币批次(提取)已成功转发到以太坊的事件

  • ValsetUpdatedEvent - 表示验证器集更新已成功转发到以太坊的事件

  • SendToInjectiveEvent - 表示已启动新的 Injective 存款的事件

  • ERC20DeployedEvent - 表示新 Cosmos 代币已在以太坊上注册的事件

Injective 的 Oracle 实现忽略以太坊上的最后 12 个区块,以确保区块最终性。实际上,这意味着最新的事件会在发生后的 2-3 分钟后被观察到。

Relayer

Relayer 将验证器集更新(或代币批次)及其确认打包成以太坊交易,并发送到 Peggy 合约。 请注意,这些消息的费用会根据以太坊 gas 价格的剧烈波动而变化,因此一个单独的批次可能需要消耗超过一百万的 gas。我们为 relayer 奖励做出的一个重要设计决策是始终在以太坊链上发放奖励。这有其缺点,尤其是在验证器集更新奖励的情况下可能会出现一些奇怪的行为。 但其优点是显而易见的,因为以太坊上的消息会支付给 msg.sender,任何现有的机器人都会在以太坊生态系统中接收并尝试提交这些消息。这使得转发市场更加竞争激烈,减少了类似秘密团体的行为。

端到端生命周期

本文档描述了 Peggy 桥接的端到端生命周期。

Peggy 智能合约部署

为了部署 Peggy 合约,必须先知道原生链(Injective 链)的验证器集。在部署 Peggy 合约套件(Peggy 实现、代理合约和 ProxyAdmin 合约)时,必须使用验证器集初始化 Peggy 合约(即代理合约)。在初始化过程中,合约会触发一个 ValsetUpdatedEvent 事件。 代理合约用于升级 Peggy 实现合约,这对于在初始阶段修复错误和进行潜在改进是必要的。它是一个简单的包装器或“代理”,用户直接与之交互,并负责将事务转发到 Peggy 实现合约,后者包含实际的逻辑。需要理解的关键概念是,实施合约可以被替换,但代理(访问点)永远不会改变。 ProxyAdmin 是 Peggy 代理的中央管理员,简化了管理工作。它控制着升级功能和所有权转移。ProxyAdmin 合约本身有一个内置的过期时间,一旦过期,便会阻止未来对 Peggy 实现合约的升级。 接下来,应更新以下 Peggy 初始化参数:

  • bridge_ethereum_address 为 Peggy 代理合约地址

  • bridge_contract_start_height 为 Peggy 代理合约部署时的区块高度

这完成了 Peggy 桥接的引导过程,链可以开始运行。之后,Operators 应该启动他们的 peggo 进程,并最终观察到最初的 ValsetUpdatedEvent 在 Injective 上被确认。

在以太坊上更新 Injective 链验证者集

img.png

验证器集 是一系列以太坊地址,附带规范化的权重,用于表示 Peggy 合约中以太坊上的 Injective 验证器集(Valset)。通过以下机制,Peggy 合约与 Injective 链的验证器集保持同步:

  1. 在 Injective 上创建新的验证器集:当以下任意情况发生时,Injective 链上会自动创建新的验证器集:

  • 当前验证器集的累计权重与上一个记录的验证器集相比,差值超过 5%

  • 某个验证器开始从验证器集解绑

  1. 在 Injective 上确认验证器集:每个 Operator 负责确认在 Injective 上创建的验证器集更新。Signer 进程通过 MsgConfirmValset 发送这些确认,方法是让验证器的委托以太坊密钥对验证器集数据的压缩表示签名。Peggy 模块验证签名的有效性,并将其持久化到状态中。

  2. 更新 Peggy 合约中的验证器集:在 2/3+1 多数验证器提交其确认后,Relayer 通过调用 updateValset 向 Peggy 合约提交新的验证器集数据。Peggy 合约随后验证数据、更新验证器集检查点、将验证器集奖励转给发送者,并触发 ValsetUpdatedEvent。

  3. 在 Injective 上确认 ValsetUpdatedEvent:Oracle 见证以太坊上的 ValsetUpdatedEvent,并发送 MsgValsetUpdatedClaim,通知 Peggy 模块验证器集已在以太坊上更新。

  4. 在 Injective 上修剪验证器集:一旦 2/3 多数的验证器对某个 ValsetUpdateEvent 提交了声明,所有先前的验证器集将从 Peggy 模块的状态中修剪掉。

  5. 验证器惩罚:在配置的时间窗口(SignedValsetsWindow)内未提供确认的验证器将面临惩罚。有关验证器惩罚的更多信息,请参阅 valset slashing。


将ERC-20代币从以太坊转移到Injective

img.png

ERC-20代币通过以下机制从以太坊转移到Injective:

  1. 将ERC-20代币存入Peggy合约:用户通过调用Peggy合约中的sendToInjective函数发起将ERC-20代币从以太坊转移到Injective的操作,向Peggy合约存入代币并触发SendToInjectiveEvent事件。存入的代币将保持锁定状态,直到在未来某个不确定的时刻被提取。该事件包含代币的数量和类型,以及接收资金的Injective链上的目标地址。

  2. 确认存款:每个Oracle见证SendToInjectiveEvent并发送MsgDepositClaim,该消息包含存款信息,发送到Peggy模块。

  3. 在Injective上铸造代币:一旦大多数验证者确认存款声明,存款将被处理。

    • 如果资产来源于以太坊,代币将在Injective链上被铸造,并转移到目标接收者的地址。

    • 如果资产来源于Cosmos-SDK,代币将被解锁并转移到目标接收者的地址。


将代币从Injective提取到以太坊

img.png
  1. 请求从Injective提取:用户可以通过向Peggy模块发送MsgSendToEth交易来发起从Injective链到以太坊的资产转移。

    1. 如果资产是以太坊原生资产,表示的代币将被销毁。

    2. 如果资产是Cosmos SDK原生资产,代币将在模块中被锁定。随后,提取请求将被加入到Outgoing Tx Pool中。

  2. 批次创建:批次创建者观察待处理的提取池。批次创建者(或任何外部第三方)通过向Injective链发送MsgRequestBatch请求为指定的代币创建批次。Peggy模块将匹配的代币类型的提取请求收集到一个批次中,并将其放入Outgoing Batch Pool。

  3. 批次确认:当检测到存在Outgoing Batch时,签名者使用其以太坊密钥对该批次进行签名,并向Peggy模块提交MsgConfirmBatch交易。

  4. 将批次提交到Peggy合约:一旦大多数验证者确认了该批次,转发者将调用Peggy合约上的submitBatch函数,提交该批次及其确认信息。Peggy合约验证签名,更新批次检查点,处理批次中的ERC-20提取请求,将批次费用转移到交易发送者,并触发TransactionBatchExecutedEvent事件。

  5. 发送提取声明到Injective:Oracles见证TransactionBatchExecutedEvent事件,并向Peggy模块发送包含提取信息的MsgWithdrawClaim。

  6. 修剪批次:一旦大多数验证者提交了他们的MsgWithdrawClaim,该批次将被删除,所有之前的批次也将被取消。取消的批次中的提取请求将被移回Outgoing Tx Pool。

  7. 批次处罚:验证者负责确认批次,如果未能完成此操作,将受到处罚。有关批次处罚的更多信息,请参阅批次处罚。

注意:虽然批次处理显著降低了单次提取的费用,但这也带来了延迟和实现复杂度。如果用户希望快速提取,他们将需要支付更高的费用。然而,这个费用将大致与非批次系统中每次提取所需的费用相同。

状态

该文档列出了Peggy模块读取/写入其状态的所有数据,以键值对(KV对)的形式。

Module Params

Params是一个模块范围的配置结构,存储参数并定义Peggy模块的整体功能。每个参数的详细规范可以在Parameters部分找到。

键
值
类型
编码

[]byte{0x4}

Module params

types.Params

Protobuf encoded

Validator Info

Ethereum Address by Validator

存储由验证者账户地址索引的委托以太坊地址。

键
值
类型
编码

[]byte{0x1} + []byte(validatorAddr)

Ethereum address

common.Address

Protobuf encoded

Validator by Ethereum Address

存储由委托以太坊地址索引的验证者账户地址。

键
值
类型
编码

[]byte{0xfb} + []byte(ethAddress)

Validator address

sdk.ValAddress

Protobuf encoded

OrchestratorValidator

当验证者希望将其投票权委托给另一个密钥时,值使用协调器地址作为键进行存储。

键
值
类型
编码

[]byte{0xe8} + []byte(AccAddress)

Orchestrator address assigned by a validator

[]byte

Protobuf encoded

Valset

这是桥接的验证者集合。由Peggy模块在EndBlocker期间自动创建。 以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。

type Valset struct {
	Nonce        uint64                               
	Members      []*BridgeValidator                   
	Height       uint64                               
	RewardAmount math.Int 
	RewardToken string
}
键
值
类型
编码

[]byte{0x2} + nonce (big endian encoded)

Validator set

types.Valset

Protobuf encoded

SlashedValsetNonce

最新的验证者集合处罚计数器。用于跟踪需要被处罚的验证者集合以及已经被处罚的验证者集合。

键
值
类型
编码

[]byte{0xf5}

Nonce

uint64

encoded via big endian

ValsetNonce

最新验证者集合的计数器。每次新的验证者集合更新时,都会更新该计数器。

键
值
类型
编码

[]byte{0xf6}

Nonce

uint64

encoded via big endian

Valset Confirmation

特定验证者集合的签名者确认。请参阅Oracle消息。

键
值
类型
编码

[]byte{0x3} + (nonce + []byte(AccAddress)

Validator Confirmation

types.MsgValsetConfirm

Protobuf encoded

Batch Confirmation

特定代币批次的签名者确认。请参阅Oracle消息。

键
值
类型
编码

[]byte{0xe1} + []byte(tokenContract) + nonce + []byte(AccAddress)

Validator Batch Confirmation

types.MsgConfirmBatch

Protobuf encoded

OutgoingTransferTx

用户的提取请求被汇集到Peggy交易池中,准备由批次创建者稍后批量处理。 每个提取请求都通过一个唯一的计数器进行索引,该计数器由Peggy模块在接收到提取请求时设置。

type OutgoingTransferTx struct {
	Id          uint64     
	Sender      string     
	DestAddress string     
	Erc20Token  *ERC20Token 
	Erc20Fee    *ERC20Token 
}
键
值
类型
编码

[]byte{0x7} + []byte("lastTxPoolId")

nonce of outgoing withdrawal

uint64

Big endian encoded

LastTXPoolID

每个由Injective接收到的提取请求的单调递增值。

键
值
类型
编码

[]byte{0x6} + []byte("lastTxPoolId")

Last used withdrawal ID

uint64

Big endian encoded

OutgoingTxBatch

OutgoingTxBatch表示同一代币类型的提取请求集合。在每次成功的MsgRequestBatch后创建。 以两种方式存储,第一种是带高度,第二种是没有高度(不安全)。没有高度的方式用于测试以及状态的导出和导入。目前,Peggy.sol 被硬编码为只接受单一代币类型的批次,并且仅以该代币类型支付奖励。

type OutgoingTxBatch struct {
	BatchNonce    uint64               
	BatchTimeout  uint64               
	Transactions  []*OutgoingTransferTx 
	TokenContract string                
	Block         uint64               
}
键
值
类型
编码

[]byte{0xa} + []byte(tokenContract) + nonce (big endian encoded)

A batch of outgoing transactions

types.OutgoingTxBatch

Protobuf encoded

[]byte{0xb} + block (big endian encoded)

A batch of outgoing transactions

types.OutgoingTxBatch

Protobuf encoded

LastOutgoingBatchID

每个由批次创建者在Injective上创建的批次的单调递增值。

键
值
类型
编码

[]byte{0x7} + []byte("lastBatchId")

Last used batch ID

uint64

Big endian encoded

SlashedBlockHeight

表示最新的处罚区块高度。始终只存储一个值。

键
值
类型
编码

[]byte{0xf7}

Latest height a batch slashing occurred

uint64

Big endian encoded

LastUnbondingBlockHeight

表示验证者开始从验证者集合中解除绑定的最新区块高度。用于确定处罚条件。

键
值
类型
编码

[]byte{0xf8}

Latest height at which a Validator started unbonding

uint64

Big endian encoded

TokenContract & Denom

一个来自计数链的代币标识符将来自一个合约。代币合约和代币标识符以两种方式存储。 首先,代币标识符作为键,值为代币合约。 其次,合约作为键,值为代币合约所代表的代币标识符。

键
值
类型
编码

[]byte{0xf3} + []byte(denom)

Token contract address

[]byte

stored in byte format

[]byte{0xf4} + []byte(tokenContract)

Token denom

[]byte

stored in byte format

LastObservedValset

此条目表示最后一次成功中继到以太坊的验证者集合(Valset)。在ValsetUpdatedEvent的确认在Injective上处理后进行更新。

键
值
类型
编码

[]byte{0xfa}

Last observed Valset on Ethereum

types.Valset

Protobuf encoded

LastEventNonce

这是在以太坊上最后一次观察到的事件的计数器。当调用TryAttestation()时设置此值。此存储中始终只持有一个值。

键
值
类型
编码

[]byte{0xf2}

Last observed event nonce

uint64

Big endian encoded

LastObservedEthereumHeight

这是在以太坊上最后一次观察到的事件的区块高度。此存储中始终只会存储一个值。

键
值
类型
编码

[]byte{0xf9}

Last observed Ethereum Height

uint64

Protobuf encoded

LastEventByValidator

这是来自特定验证者的最后一个观察到的以太坊事件。每次关联的协调器发送事件声明时都会更新。

type LastClaimEvent struct {
    EthereumEventNonce  uint64 
    EthereumEventHeight uint64 
}
键
值
类型
编码

[]byte{0xf1} + []byte(validator address)

Last observed event by some Validator

types.LastClaimEvent

Protobuf encoded

Attestation

确认是多个声明的汇总,随着更多的投票(声明)到来,最终被所有协调器观察到。一旦被观察到,该声明的特定逻辑将被执行。 每个确认都绑定到一个唯一的事件计数器(由Peggy合约生成),并且必须按顺序处理。这是一个正确性问题,如果事务顺序被打乱,则可能会发生交易重放攻击。

type Attestation struct {
	Observed bool       
	Votes    []string   
	Height   uint64     
	Claim    *types.Any 
}
键
值
类型
编码

[]byte{0x5} + event nonce (big endian encoded) + []byte(claimHash)

Attestation of occurred events/claims

types.Attestation

Protobuf encoded

PastEthSignatureCheckpoint

一个计算出的哈希值,表明某个验证者集合/代币批次在Injective上确实存在。此检查点也存在于Peggy合约中。在每次新的验证者集合更新和代币批次创建时都会更新。

键
值
类型
编码

[]byte{0x1b}

Last created checkpoint hash on Injective

gethcommon.Hash

store in byte format

EthereumBlacklist

已知的恶意以太坊地址列表,这些地址被禁止使用桥接。

键
值
类型
编码

[]byte{0x1c} + []byte(ethereum address)

Empty []byte slice

gethcommon.Hash

stored in byte format]

消息

这是Peggy消息类型的参考文档。有关代码参考和精确参数,请参阅proto定义。

User Messages

这些是最终用户通过Injective链Peggy模块发送的消息。有关整个存款和提取过程的更详细概述,请参阅工作流程。

SendToEth

当用户希望将资金提取回以太坊时,发送到Injective。提交的金额会立即从用户的余额中扣除。该提取请求作为types.OutgoingTransferTx被添加到外发交易池中,直到它被包含在一个批次中。

type MsgSendToEth struct {
	Sender    string    // sender's Injective address
	EthDest   string    // receiver's Ethereum address
	Amount    types.Coin    // amount of tokens to bridge
	BridgeFee types.Coin    // additional fee for bridge relayers. Must be of same token type as Amount
}

CancelSendToEth

此消息允许用户取消尚未批量处理的特定提取请求。用户余额将被退还(金额 + 桥接费用)。

type MsgCancelSendToEth struct {
	TransactionId uint64    // unique tx nonce of the withdrawal
	Sender        string    // original sender of the withdrawal
}

SubmitBadSignatureEvidence

此调用允许任何人提交证据,证明某个验证者签署了一个从未存在的验证者集合或批次。主体包含该批次或验证者集合。

type MsgSubmitBadSignatureEvidence struct {
	Subject   *types1.Any 
	Signature string      
	Sender    string      
}

Batch Creator Messages

这些消息由Peggy的批次创建子过程发送。

RequestBatch

每当某个批次创建者发现汇集的提取请求,且在批量处理后满足其最低批次费用(PEGGO_MIN_BATCH_FEE_USD)时,会发送此消息。接收到此消息后,Peggy模块会收集所有请求的代币标识符的提取请求,创建一个唯一的代币批次(types.OutgoingTxBatch),并将其放入外发批次池中。已批量处理的提取请求无法通过MsgCancelSendToEth取消。

type MsgRequestBatch struct {
	Orchestrator string // orchestrator address interested in creating the batch. Not permissioned.  
	Denom        string // the specific token whose withdrawals will be batched together
}

Oracle Messages

这些消息由Peggy的Oracle子过程发送。

DepositClaim

当Peggy合约触发SendToInjectiveEvent事件时,发送到Injective。每当用户从以太坊向Injective进行单独存款时,就会发生这种情况。

type MsgDepositClaim struct {
	EventNonce     uint64   // unique nonce of the event                                
	BlockHeight    uint64   // Ethereum block height at which the event was emitted                                
	TokenContract  string   // contract address of the ERC20 token                                 
	Amount         sdkmath.Int  // amount of deposited tokens 
	EthereumSender string   // sender's Ethereum address                                 
	CosmosReceiver string   // receiver's Injective address                                 
	Orchestrator   string   // address of the Orchestrator which observed the event                               
}

WithdrawClaim

当Peggy合约触发TransactionBatchExecutedEvent事件时,发送到Injective。这发生在转发者成功调用合约上的submitBatch函数以完成一批提取请求时。

type MsgWithdrawClaim struct {
	EventNonce    uint64    // unique nonce of the event
	BlockHeight   uint64    // Ethereum block height at which the event was emitted
	BatchNonce    uint64    // nonce of the batch executed on Ethereum
	TokenContract string    // contract address of the ERC20 token
	Orchestrator  string    // address of the Orchestrator which observed the event
}

ValsetUpdatedClaim

当Peggy合约触发ValsetUpdatedEvent事件时,发送到Injective。这发生在转发者成功调用合约上的updateValset函数以更新以太坊上的验证者集合时。


type MsgValsetUpdatedClaim struct {
	EventNonce   uint64 // unique nonce of the event                      
	ValsetNonce  uint64 // nonce of the valset                           
	BlockHeight  uint64 // Ethereum block height at which the event was emitted                           
	Members      []*BridgeValidator // members of the Validator Set               
	RewardAmount sdkmath.Int // Reward for relaying the valset update 
	RewardToken  string // reward token contract address                                 
	Orchestrator string // address of the Orchestrator which observed the event                                 
}

ERC20DeployedClaim

当Peggy合约触发ERC20DeployedEvent事件时,发送到Injective。这发生在合约上调用deployERC20方法以发行一个新的符合桥接条件的代币资产时。

type MsgERC20DeployedClaim struct {
	EventNonce    uint64    // unique nonce of the event
	BlockHeight   uint64    // Ethereum block height at which the event was emitted
	CosmosDenom   string    // denom of the token
	TokenContract string    // contract address of the token
	Name          string    // name of the token
	Symbol        string    // symbol of the token
	Decimals      uint64    // number of decimals the token has
	Orchestrator  string    // address of the Orchestrator which observed the event
}

Signer Messages

这些消息由Peggy的签名者子过程发送。

ConfirmBatch

当签名者发现一个批次尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的批次提供确认,否则他们将被处罚。

type MsgConfirmBatch struct {
	Nonce         uint64    // nonce of the batch 
	TokenContract string    // contract address of batch token
	EthSigner     string    // Validator's delegated Ethereum address (previously registered)
	Orchestrator  string    // address of the Orchestrator confirming the batch
	Signature     string    // Validator's signature of the batch
}

ValsetConfirm

当签名者发现一个验证者集合更新尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到Injective。验证者最终必须为已创建的验证者集合更新提供确认,否则他们将被处罚。

type MsgValsetConfirm struct {
	Nonce        uint64 // nonce of the valset 
	Orchestrator string // address of the Orchestrator confirming the valset
	EthAddress   string // Validator's delegated Ethereum address (previously registered)
	Signature    string // Validator's signature of the valset
}

Relayer Messages

转发者不会向Injective发送任何消息,而是构造包含Injective数据的以太坊交易,通过submitBatch和updateValset方法更新Peggy合约。

Validator Messages

这些是直接使用验证者的消息密钥发送的消息。

SetOrchestratorAddresses

由管理验证者节点的操作员发送到Injective。在能够启动他们的协调器(peggo)进程之前,他们必须注册一个选定的以太坊地址,以代表其验证者在以太坊上的身份。可选地,可以提供一个额外的Injective地址(Orchestrator字段)以代表该验证者在桥接过程中的身份(peggo)。如果省略,则默认为验证者的自身地址。

type MsgSetOrchestratorAddresses struct {
	Sender       string // address of the Injective validator
	Orchestrator string // optional Injective address to represent the Validator in the bridging process (Defaults to Sender if left empty)
	EthAddress   string // the Sender's (Validator) delegated Ethereum address
}

此消息设置协调器的委托密钥。

处罚

安全考虑

验证者集合是实际拥有质押的密钥集合,这些密钥会因双重签名或其他不当行为而被处罚。我们通常将链的安全性视为验证者集合的安全性。每条链的情况不同,但这是我们的黄金标准。即使是IBC,其安全性也仅限于参与的两个验证者集合中的最小值。

Eth桥中继是与main injective daemon 一起运行的二进制文件,由验证者集合管理。它纯粹作为代码组织的一部分,负责签署以太坊交易,并观察以太坊上的事件并将其引入 Injective 链状态。它使用以太坊密钥签署发送到以太坊的交易,并使用 Injective 链账户密钥签署来自以太坊的事件。我们可以对任何由验证者集合运行的Eth签名者签署的错误消息添加处罚条件,并能够提供与验证者集合相同的安全性,只是由不同的模块检测恶意证据并决定处罚的程度。如果我们能证明由验证者集合的任何Eth签名者签署的交易是非法或恶意的,那么我们可以在 Injective 链侧进行处罚,并可能提供验证者集合的100%安全性。请注意,这还可以访问3周的解锁期,即使他们立即解锁,也允许证据进行处罚。

以下是我们在Peggy中使用的各种削减条件。

PEGGYSLASH-01:签署虚假的验证者集合或交易批次证据

该惩罚条件旨在阻止验证者对从未存在于 Injective Chain 上的验证者集合(validator set)和随机数(nonce)进行签名。其通过一种证据机制实现,任何人都可以提交一条消息,其中包含验证者对伪造的验证者集合的签名。此机制旨在实现以下效果:如果一个验证者联盟意图提交伪造的验证者集合,其中一名叛变者可能导致整个联盟的验证者受到惩罚(slashed)。

// This call allows anyone to submit evidence that a
// validator has signed a valset, batch, or logic call that never
// existed. Subject contains the batch, valset, or logic call.
type MsgSubmitBadSignatureEvidence struct {
	Subject   *types1.Any 
	Signature string      
	Sender    string      
}

实现注意事项:

该惩罚条件最复杂的部分在于确定某个验证者集合是否从未存在于 Injective 上。为了节省空间,我们需要清理旧的验证者集合。我们可以在键值存储(KV store)中维护一个验证者集合哈希到布尔值的映射,并利用该映射来检查某个验证者集合是否曾经存在。这种方法比存储整个验证者集合更高效,但其增长仍然是无界的。或许可以使用其他加密方法来减少该映射的大小。此外,可以考虑从该映射中删除非常旧的条目,但任何删除操作都会降低该惩罚条件的威慑力。

当前实现的版本存储了所有过去事件的哈希映射,这比存储完整的批次或验证者集合更小,并且不需要频繁访问。一个可能但尚未实现的效率优化是,在一定时间后从该列表中移除哈希值。但这需要为每个哈希存储更多的元数据。

目前,中继器(relayer)尚未实现自动证据提交功能。当签名在以太坊上可见时,再进行惩罚已经无法防止桥接劫持或资金盗窃。此外,由于执行此操作需要验证者集合中 66% 的多数同意,控制多数的一方可能会直接拒绝提交证据。该惩罚条件最常见的应用场景是破坏试图控制桥接的验证者联盟,通过增加他们之间的信任难度,使其难以协调实施此类盗窃行为。

盗窃行为将涉及交换可惩罚的以太坊签名,这为联盟中的任何叛变者手动提交此类消息提供了可能性。

目前,该功能在状态中实现为一个不断增长的哈希数组。

PEGGYSLASH-02: 未能签署交易批次

当验证者在交易批次(transaction batch)由 Peggy 模块创建后的 SignedBatchesWindow 时间窗口内未能对其进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:

  1. 验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。

  2. 验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何交易批次都无法获得足够的签名以提交至 Peggy 以太坊合约。

PEGGYSLASH-03: 未能签署验证者集合更新

当验证者未能对由 Peggy 模块生成的验证者集合更新(validator set update)进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:

  1. 验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。

  2. 验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何验证者集合更新都无法获得足够的签名以提交至 Peggy 以太坊合约。如果他们阻止验证者集合更新的时间超过 Injective Chain 的解绑周期(unbonding period),他们将不再因提交伪造的验证者集合更新和交易批次(PEGGYSLASH-01 和 PEGGYSLASH-03)而受到惩罚。

为了应对第二种情况,PEGGYSLASH-03 还需要惩罚那些已停止验证但仍处于解绑周期内的验证者,惩罚时间最长可达 UnbondSlashingValsetsWindow 区块。这意味着当验证者离开验证者集合时,他们需要至少保持其设备运行 UnbondSlashingValsetsWindow 个区块。这对于 Injective Chain 来说是不常见的,可能不会被验证者接受。

当前 UnbondSlashingValsetsWindow 的值为 25,000 个区块,大约为 12-14 小时。我们根据以下逻辑确定这是一个安全的值:只要每个离开验证者集合的验证者至少签署一个不包含他们的验证者集合更新,就能保证中继器(relayer)能够生成一系列验证者集合更新,将链上的当前状态转换为最新状态。

需要注意的是,如果能够在共识代码内部执行以太坊签名,PEGGYSLASH-02 和 PEGGYSLASH-03 都可以被取消而不会降低安全性。这是 Tendermint 的一个较为有限的功能增强,但将显著减少 Peggy 的惩罚风险。

PEGGYSLASH-04: 提交错误的以太坊预言机声明(当前已禁用)

以太坊预言机代码(目前主要集中在 attestation.go 文件中)是 Peggy 模块的关键组成部分。它使 Peggy 模块能够了解以太坊上发生的事件,例如存款(deposits)和已执行的交易批次(executed batches)。PEGGYSLASH-03 旨在惩罚那些提交了从未在以太坊上发生的事件的声明的验证者。

实现注意事项

我们判断一个事件是否在以太坊上发生的唯一方式是通过以太坊事件预言机本身。因此,为了实现此惩罚条件,我们会惩罚那些在与 >2/3 验证者观察到的事件具有相同随机数(nonce)的情况下提交了不同事件声明的验证者。

尽管初衷良好,但该惩罚条件可能不适用于大多数 Peggy 应用场景。这是因为它将安装 Peggy 的 Injective Chain 的正常运行与以太坊链的正常运行绑定在一起。如果以太坊链发生严重分叉,不同验证者可能会在同一事件随机数下看到不同的事件,并因此受到惩罚,尽管他们并无过错。大规模的不公平惩罚将对 Injective Chain 的社会结构造成严重破坏。

PEGGYSLASH-04 的必要性分析

该惩罚条件的实际效用在于,如果 >2/3 的验证者形成一个联盟,在某个随机数下提交虚假事件声明,联盟中的部分成员可能会叛变并在同一随机数下提交真实事件声明。如果有足够多的联盟成员叛变,使得真实事件被观察到,那么剩余的联盟成员将因此惩罚条件受到惩罚。然而,这通常需要超过一半的联盟成员叛变。

如果没有足够多的联盟成员叛变,则两个事件都不会被观察到,以太坊预言机将停止运行。这种情况比 PEGGYSLASH-04 实际被触发的场景更为可能。

此外,在联盟成功的情况下,PEGGYSLASH-04 可能会针对诚实验证者触发。这可能使正在形成的联盟更容易威胁那些不愿加入的验证者。

PEGGYSLASH-05: 未能提交以太坊预言机声明(当前已禁用)

PEGGYSLASH-05 与 PEGGYSLASH-04 类似,但其针对的是未提交已被观察到的预言机声明(oracle claim)的验证者。与 PEGGYSLASH-04 不同,PEGGYSLASH-05 旨在惩罚那些完全停止参与预言机机制的验证者。

实现注意事项

遗憾的是,PEGGYSLASH-05 与 PEGGYSLASH-04 存在相同的缺点,即它将 Injective Chain 的正确运行与以太坊链的运行绑定在一起。此外,它可能无法有效激励正确的行为。为了避免触发 PEGGYSLASH-05,验证者只需复制接近被观察到的声明即可。虽然可以通过提交-揭示机制(commit-reveal scheme)来防止这种复制行为,但“懒惰的验证者”仍然可以轻松使用公共的以太坊全节点或区块浏览器,这对安全性影响类似。因此,PEGGYSLASH-05 的实际效用可能非常有限。

PEGGYSLASH-05 还引入了显著的风险,主要围绕以太坊链的分叉问题。例如,最近 OpenEthereum 未能正确处理柏林硬分叉(Berlin hardfork),由此导致的节点“故障”对自动化工具完全不可检测。节点并未崩溃,因此无需重启,区块仍在生成,尽管速度极慢。如果这种情况发生在 Peggy 运行且 PEGGYSLASH-05 激活的情况下,将导致这些验证者被从集合中移除,可能会引发链的混乱时刻,因为数十个验证者因几乎无过错而被移除。

在没有 PEGGYSLASH-04 和 PEGGYSLASH-05 的情况下,以太坊事件预言机仅在 >2/3 的验证者自愿提交正确声明时继续运行。尽管反对 PEGGYSLASH-04 和 PEGGYSLASH-05 的论点很有说服力,但我们必须决定是否能够接受这一事实。或者,我们必须接受 Injective Chain 可能因以太坊生成的因素而完全停止的可能性。

EndBlock

在每个区块结束时,将对模块的状态执行以下操作:

1. 处罚

验证者处罚

当验证者未能对通过 SignedValsetsWindow 的验证者集合更新(valset update)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为验证者集合更新提供确认,他们将因其抵押的 SlashFractionValset 部分被罚没,并立即被监禁(jailed)。

批量处罚

当验证者未能对通过 SignedBatchesWindow 的交易批次(batch)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为交易批次提供确认,他们将因其抵押的 SlashFractionBatch 部分被罚没,并立即被监禁(jailed)。

2. 取消超时的交易批次

任何仍在 Outgoing Batch 池中且其 BatchTimeout(指定的以太坊区块高度,交易批次应在此高度之前执行)已超时的交易批次将被从池中移除,相关的提款请求将重新插入到 Outgoing Tx 池中。

3. 创建新的验证者集合更新

在以下情况下,将自动创建新的验证者集合更新(Validator Set update):

  1. 验证者权力变化超过 5%:当最新验证者集合与当前验证者集合之间的权力差异(power diff)超过 5% 时。

  2. 验证者开始解绑:当某个验证者开始解绑(unbonding)时。

新的验证者集合最终将被中继(relayed)至以太坊上的 Peggy 合约。

4. 修剪旧验证者集

已通过 SignedValsetsWindow 的先前观察到的验证者集合(valsets)将从状态中移除。

5. 证明处理

处理当前正在投票的所有证明(attestations)(针对特定事件的声明集合)。每个证明依次处理,以确保每个 Peggy 合约事件都被处理。在处理完每个证明后,模块的 lastObservedEventNonce 和 lastObservedEthereumBlockHeight 将被更新。

根据证明中的声明类型,执行以下操作:

  • MsgDepositClaim:为接收者地址铸造/解锁存入的代币。

  • MsgWithdrawClaim:从 Outgoing 池中移除相应的交易批次,并取消任何先前的批次。

  • MsgValsetUpdatedClaim:更新模块的 LastObservedValset。

  • MsgERC20DeployedClaim:验证新的代币元数据并将其注册到模块的状态中(denom <-> token_contract)。

6. 清理已处理的证明

之前处理过的证明(高度早于 lastObservedEthereumBlockHeight)会从模块状态中移除。

事件

peggy 模块会触发以下事件:

EndBlocker

EventAttestationObserved

类型
属性键
属性值

int32

attestation_type

{attestation_type}

string

bridge_contract

{bridge_contract_address}

uint64

bridge_chain_id

{bridge_chain_id}

[]byte

attestation_id

{attestation_id}

uint64

nonce

{event_nonce}

EventValidatorSlash

类型
属性键
属性值

string

reason

{reason_for_slashing}

int64

power

{validator_power}

string

consensus_address

{consensus_addr}

string

operator_address

{operator_addr}

string

moniker

{validator_moniker}

Handler

EventSetOrchestratorAddresses

类型
属性键
属性值

string

validator_address

{validator_addr}

string

orchestrator_address

{orchestrator_addr}

string

operator_eth_address

{eth_addr}

EventSendToEth

类型
属性键
属性值

message

outgoing_tx_id

{tx_id}

string

sender

{sender_addr}

string

receiver

{dest_addr}

sdk.Coin

amount

{token_amount}

sdk.Coin

bridge_fee

{token_amount}

EventBridgeWithdrawCanceled

类型
属性键
属性值

withdrawal_cancelled

bridge_contract

{bridge_contract}

withdrawal_cancelled

bridge_chain_id

{bridge_chain_id}

EventOutgoingBatch

类型
属性键
属性值

string

denom

{token_denom}

string

orchestrator_address

{orch_addr}

uint64

batch_nonce

{batch_nonce}

uint64

batch_timeout

{block_height}

[]uint64

batch_tx_ids

{ids}

EventOutgoingBatchCanceled

类型
属性键
属性值

string

bridge_contract

{bridge_contract}

uint64

bridge_chain_id

{bridge_chain_id}

uint64

batch_id

{id}

uint64

nonce

{nonce}

EventValsetConfirm

类型
属性键
属性值

uint64

valset_nonce

{nonce}

string

orchestrator_address

{prch_addr}

EventConfirmBatch

类型
属性键
属性值

uint64

batch_nonce

{nonce}

string

orchestrator_address

{orch_addr}

EventDepositClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

string

ethereum_sender

{sender_addr}

string

cosmos_receiver

{receiver_addr}

string

token_contract

{contract_addr}

sdk.Int

amount

{token_amount}

string

orchestrator_address

{orch_addr}

string

data

{custom_data}

EventWithdrawClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce{

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

uint64

batch_nonce

{batch_nonce}

string

token_contract

{contract_addr}

string

orchestrator_address

{orch_addr}

EventERC20DeployedClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

string

cosmos_denom

{token_denom}

string

token_contract

{token_conntract_addr}

string

name

{token_name}

string

symbol

{token_symbol}

uint64

decimals

{token_decimals}

string

orchestrator_address

{orch_addr}

EventValsetUpdateClaim

类型
属性键
属性值

uint64

event_nonce

{event_nonce}

uint64

event_height

{event_height}

[]byte

attestation_id

{attestation_key}

uint64

valset_nonce

{valset_nonce}

[]*BridgeValidator

valset_members

{array_of_validators}

sdk.Int

reward_amount

{amount}

string

reward_token

{contract_addr}

string

orchestrator_address

{orch_addr}

参数

本文档描述并建议配置 peggy 模块的参数。默认参数可以在 peggy 模块的 genesis.go 文件中找到。

type Params struct {
	PeggyId                       string                                 
	ContractSourceHash            string                                 
	BridgeEthereumAddress         string                                 
	BridgeChainId                 uint64                                 
	SignedValsetsWindow           uint64                                 
	SignedBatchesWindow           uint64                                 
	SignedClaimsWindow            uint64                                 
	TargetBatchTimeout            uint64                                 
	AverageBlockTime              uint64                                 
	AverageEthereumBlockTime      uint64                                 
	SlashFractionValset           math.LegacyDec 
	SlashFractionBatch            math.LegacyDec 
	SlashFractionClaim            math.LegacyDec 
	SlashFractionConflictingClaim math.LegacyDec 
	UnbondSlashingValsetsWindow   uint64  
	SlashFractionBadEthSignature  math.LegacyDec 
	CosmosCoinDenom               string  
	CosmosCoinErc20Contract       string  
	ClaimSlashingEnabled          bool    
	BridgeContractStartHeight     uint64  
	ValsetReward                  types.Coin
}

peggy_id

一个随机的 32 字节值,用于防止签名重用。例如,如果 Injective Chain 验证者决定将相同的 Ethereum 密钥用于另一个也运行 Peggy 的链,我们不希望能够将链 A 的存款回放到链 B 的 Peggy 上。这个值在 Ethereum 上使用,因此必须在启动前在 genesis.json 中设置,并且在部署 Peggy 后不得更改。部署 Peggy 后更改此值会导致桥接功能失效。要恢复,只需将其设置回合同部署时的原始值。

contract_source_hash

已知良好版本的 Peggy 合约 Solidity 代码的代码哈希。可以用来验证已部署的合约是否为正确版本。这是一个仅供治理操作参考的值,Peggy 代码永远不会读取它。

bridge_ethereum_address

是 Ethereum 侧桥接合约的地址,这只是一个仅供治理操作参考的值,实际上不会被任何 Peggy 模块代码使用。 Ethereum 桥接中继器使用此值与 Peggy 合约进行交互,用于查询事件和提交 valset/batches 到 Peggy 合约。

bridge_chain_id

桥接链 ID 是 Ethereum 链的唯一标识符。这个值仅供参考,实际上不会被任何 Peggy 代码使用。 这些参考值可能会被未来的 Peggy 客户端实现用于一致性检查。

Signing windows

  • signed_valsets_window

  • signed_batches_window

  • signed_claims_window

这些值表示验证者必须在多少个区块时间内提交批次或 valset 的签名,或者提交特定证明 nonce 的声明。 在证明的情况下,这个时钟从证明创建时开始,但仅在事件经过后才允许进行惩罚。请注意,声明惩罚当前尚未启用,详见惩罚规范。

target_batch_timeout

这是批次超时的“目标”值,之所以称为目标,是因为 Ethereum 是一个概率链,无法提前确定区块频率。

Ethereum timing

  • average_block_time

  • average_ethereum_block_time

这些值分别是 Injective Chain 和 Ethereum 的平均区块时间,用于计算目标批次超时。若区块生成时间发生任何重大且长期的变化,治理需要更新这些值。

Slash fractions

  • slash_fraction_valset

  • slash_fraction_batch

  • slash_fraction_claim

  • slash_fraction_conflicting_claim

针对各种 Peggy 相关的惩罚条件的惩罚分数。前三个分数是指未提交特定消息,第三个是指未提交声明,最后一个是指提交与其他验证者不同的声明。 请注意,如惩罚规范所述,声明惩罚当前已禁用。

valset_reward

Valset 奖励是当中继者将 valset 中继到 Ethereum 上的 Peggy 合约时,支付给中继者的奖励金额。

中继语义

本文档旨在帮助开发者实现替代的 Peggy 中继器。Orchestrator 的两个主要组件与 Ethereum 进行交互。Peggy 桥接的设计侧重于提高效率,而非易用性。这意味着,外部二进制文件有许多隐含的要求,本文档尽力将这些要求明确化。 Peggy Orchestrator 结合了在 Peggy 桥接中需要由外部二进制文件执行的三个不同角色。本文档重点介绍了作为 Orchestrator 一部分的中继器角色的要求。

验证者集更新中继的语义

验证者集和签名的排序与顺序

在更新 Peggy 合约中的验证者集时,必须提供旧验证者集的副本。这个副本 只能 从 Ethereum 链上的最后一个 ValsetUpdated 事件中获取。 提供旧验证者集是存储优化的一部分,代替将整个验证者集存储在 Ethereum 存储中,它由每个调用者提供并存储在更便宜的 Ethereum 事件队列中。Peggy 合约中不执行任何排序操作,这意味着验证者列表及其新签名必须以与上次调用完全相同的顺序提交。 为了正常操作,此要求可以简化为“按降序排序验证者的权重,并在权重相等时按 Ethereum 地址字节排序”。由于 peggy 模块生成验证者集,因此它们应该始终按顺序排列。中继器无法更改此顺序,因为它是签名的一部分。但如果 peggy 模块侧的排序方法发生变化,将会停止验证者集更新,并本质上使桥接断开,除非您的实现足够智能,能够查看最后提交的顺序,而不是盲目遵循排序。

决定中继哪个验证者集

Injective Chain 仅生成一系列的验证者集,并不对它们如何被中继做出任何判断。由中继器实现决定如何优化此中继操作的 gas 成本。 例如,假设我们有验证者集 A、B、C 和 D,每个验证者集是在 peggy 验证者集存储中的最后一个快照与当前激活的验证者集之间存在 5% 权重差异时创建的。 5% 是一个任意常数。这里选择的具体值是链在 Ethereum 验证者集更新的实时性和保持其更新的成本之间做出的折中。这个值越高,投票验证者集所需的比例越低,以在最坏情况下劫持桥接。如果每个区块都进行新的验证者集更新,那么 66% 的验证者需要合谋,而 5% 的变化阈值意味着,在给定的验证者集中,61% 的总投票权合谋可能会窃取桥接中的资金。

A -> B -> C -> D
     5%  10%   15%

中继器应当遍历 Peggy Ethereum 合约的事件历史,确定验证者集 A 当前位于 Peggy 桥接中。它可以选择中继验证者集 B、C,然后是 D,或者仅提交验证者集 D。只要所有验证者都已签署 D,并且它的投票权超过 66%,它就能独立通过,而无需为中继中间集支付可能数百美元的额外 Ethereum gas 费用。 在提交交易之前在本地执行此检查是实现成本效益中继器的关键。您可以使用本地的 Ethereum 签名实现并自行计算权重和签名,或者可以简单地使用 eth_call() Ethereum RPC 在您的 Ethereum 节点上模拟该调用。 请注意,eth_call() 经常会有一些陷阱。如果没有 Ethereum 来支付 gas,基于 Geth 的实现中的所有调用都会失败,而基于 Parity 的实现则通常会忽略您的 gas 输入,并返回准确的 gas 使用量。

交易批次中继的语义

为了提交交易批次,您还需要提交最后一组验证者及其质押权重。这是为了实现前面提到的相同存储优化。

决定中继哪个批次

决定中继哪个批次与决定中继哪个验证者集非常不同。批次中继主要是由费用驱动,而不是为了维护桥接的完整性。因此,这一决策主要依赖于费用计算,而这一点又被“批次请求”的概念进一步复杂化。批次请求是一个无权限的事务,请求 peggy 模块为特定的代币类型生成一个新的批次。

批次请求的设计目的是允许用户随时从发送到 Ethereum 的交易池中提取他们的代币,直到中继者有兴趣实际中继这些交易为止。当交易仍在池中时,如果用户通过发送 MsgCancelSendToEth 被允许撤回交易,则不存在双重支付的风险。一旦交易因“请求批次”而进入批次,情况就不一样了,用户的资金必须保持锁定,直到 Oracle 通知 peggy 模块,包含用户代币的批次已经变得无效或者已经在 Ethereum 上执行。

中继者使用查询端点 BatchFees 来遍历每个代币类型的发送到 Ethereum 交易池,然后中继者可以观察正在中继的 ERC-20 代币在去中心化交易所(DEX)上的价格,并计算执行该批次的 gas 费用(通过 eth_call())以及如果需要的话,在 DEX 上清算收益的 gas 费用。一旦中继者确定一个批次是合适且有利可图的,它可以发送 MsgRequestBatch,然后该批次会被创建,供中继者中继。

对于现有的批次,中继者也应该评估其盈利性,并使用类似的方法尝试中继。

改进

原生 Ethereum 签名

验证者在 Peggy Orchestrator 中运行所需的 Eth Signer,因为目前我们无法在不对 Tendermint 进行重大修改的情况下,将这种简单的签名逻辑插入到基于 Cosmos SDK 的链中。未来,随着对 Tendermint 的修改,这可能成为可能。

需要注意的是,如果能够在共识代码中执行 Ethereum 签名,则可以消除 PEGGYSLASH-02 和 PEGGYSLASH-03,而不会损失安全性。这是一个相对有限的功能增强,可以使 Peggy 更不容易受到惩罚。

改进的验证者激励措施

目前,Peggy 中的验证者只有一个正向激励——桥接正常运行所带来的额外活动对链的促进。 另一方面,验证者必须注意许多负向激励(惩罚)。这些在惩罚规范中有所概述。 一个未在惩罚中涵盖的负向激励是提交预言机提交和签名的成本。目前,这些操作没有激励,但验证者仍然需要支付费用进行提交。考虑到当前 Injective Chain 上相对便宜的交易费用,这不是一个严重的问题,但随着交易费用的上涨,这无疑是一个需要考虑的重要因素。 对于正确参与桥接操作的验证者,应该考虑一些正向激励措施,除了消除强制性提交的费用之外。