peggy模块使Injective链能够支持一个可信的、链上双向ERC-20代币桥接到以太坊。在该系统中,以太坊上的ERC-20代币持有者可以将他们的ERC-20代币转换为Injective链上的Cosmos原生币,反之亦然。 这个去中心化的桥接由Injective链的验证者进行安全保障和操作。
Peggy 智能合约(Ethereum)
Peggy 模块(Injective Chain)
Peggo(链下中继器,又称orchestrator
)
Oracle
(监测Peggy
合约事件并向Peggy
模块发送声明)
EthSigner
(对Valset
和批量确认进行签名并发送至Peggy
模块)
Batch Requester
(向Peggy
模块发送批量代币提取请求)
Valset Relayer
(向Peggy
合约提交验证人集合更新)
Batch Relayer
(向Peggy
合约提交批量代币提取)
此外,Injective Chain
验证人除了运行injectived
节点以签署区块外,还必须运行peggo orchestrator
,以在Ethereum
上的Peggy
智能合约与Injective Chain
上的Peggy
模块之间中继数据。
在Ethereum
上维护Injective Chain
验证人集合的最新检查点
从Ethereum
向Injective Chain
转移ERC-20
代币
从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
示例)
这两个实体共同完成三件事:
将代币资产从以太坊转移到Injective
将代币资产从Injective转移到以太坊
保持Peggy.sol
合约与Injective上的活动验证者集同步
即使不成为验证者,也可以运行peggo
。当配置为使用与验证者地址无关的地址时,peggo
会自动以“中继者模式”运行。在这种模式下,只有两件事可以发生:
可以在Injective上创建新的代币批次
已确认的验证者集/批次可以被转发到以太坊
任何来自以太坊的资产,只要实现了ERC-20标准,都可以通过调用sendToInjective
函数在Peggy.sol
合约中从以太坊转移到Injective,该函数将代币从发送者的余额转移到Peggy合约。
所有Operators
都运行他们的peggo
进程,这些进程提交MsgDepositClaim
消息,描述它们观察到的存款。一旦超过66%的所有投票权提交了该特定存款的声明,代表代币就会被铸造并发行到发送者请求的Injective链地址。
这些代表代币的面额前缀为peggy
,后接ERC-20代币的十六进制地址,例如peggy0xdac17f958d2ee523a2206206994597c13d831ec7
。
来自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
协调器进程由四个子进程组成,这些子进程在精确的时间间隔内并发运行(循环)。它们分别是:
Signer:使用操作员的以太坊密钥签署新的验证者集更新和代币批次,并通过消息提交。
Oracle:观察以太坊事件并将其作为声明发送到Injective。
Relayer:将已确认的验证者集更新和代币批次提交到以太坊上的Peggy
合约。
Batch Creator:观察Injective上的(新)提款,并根据其类型和配置的PEGGO_MIN_BATCH_FEE_USD
值决定哪些提款需要打包。
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 的职责是提供确认,证明一个操作员(Orchestrator
)参与了桥接活动。如果未能提供这些确认,将导致该操作员的验证节点遭受惩罚。换句话说,这个过程必须始终运行在验证节点上。
任何在 Injective->Ethereum 流水线中移动的负载(如验证器集更新/代币批次)都需要验证器签名才能成功转发到以太坊。某些在 Peggy 合约上的调用接受一组签名,并与合约中的验证器集进行比对。Orchestrators
使用他们的委托以太坊地址来生成这些签名:这是操作员在初始设置时决定的以太坊地址(SetOrchestratorAddress
)。这个地址代表了该验证器在以太坊区块链上的身份,并将作为多签名成员添加,拥有尽可能接近 Injective 链投票权的加权投票权。
每当 Signer 发现 Peggy 模块中存在未确认的验证器集更新(代币批次)时,它会发出 MsgConfirmValset
(MsgConfirmBatch
)作为证明,证明该操作验证器在桥接活动中是活跃的。
监控以太坊网络中涉及 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
将验证器集更新(或代币批次)及其确认打包成以太坊交易,并发送到 Peggy 合约。
请注意,这些消息的费用会根据以太坊 gas 价格的剧烈波动而变化,因此一个单独的批次可能需要消耗超过一百万的 gas。我们为 relayer 奖励做出的一个重要设计决策是始终在以太坊链上发放奖励。这有其缺点,尤其是在验证器集更新奖励的情况下可能会出现一些奇怪的行为。
但其优点是显而易见的,因为以太坊上的消息会支付给 msg.sender
,任何现有的机器人都会在以太坊生态系统中接收并尝试提交这些消息。这使得转发市场更加竞争激烈,减少了类似秘密团体的行为。
本文档描述了 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 上被确认。
验证器集 是一系列以太坊地址,附带规范化的权重,用于表示 Peggy 合约中以太坊上的 Injective 验证器集(Valset)。通过以下机制,Peggy 合约与 Injective 链的验证器集保持同步:
在 Injective 上创建新的验证器集:当以下任意情况发生时,Injective 链上会自动创建新的验证器集:
当前验证器集的累计权重与上一个记录的验证器集相比,差值超过 5%
某个验证器开始从验证器集解绑
在 Injective 上确认验证器集:每个 Operator 负责确认在 Injective 上创建的验证器集更新。Signer 进程通过 MsgConfirmValset
发送这些确认,方法是让验证器的委托以太坊密钥对验证器集数据的压缩表示签名。Peggy 模块验证签名的有效性,并将其持久化到状态中。
更新 Peggy 合约中的验证器集:在 2/3+1 多数验证器提交其确认后,Relayer 通过调用 updateValset
向 Peggy 合约提交新的验证器集数据。Peggy 合约随后验证数据、更新验证器集检查点、将验证器集奖励转给发送者,并触发 ValsetUpdatedEvent
。
在 Injective 上确认 ValsetUpdatedEvent
:Oracle 见证以太坊上的 ValsetUpdatedEvent
,并发送 MsgValsetUpdatedClaim
,通知 Peggy 模块验证器集已在以太坊上更新。
在 Injective 上修剪验证器集:一旦 2/3 多数的验证器对某个 ValsetUpdateEvent
提交了声明,所有先前的验证器集将从 Peggy 模块的状态中修剪掉。
验证器惩罚:在配置的时间窗口(SignedValsetsWindow
)内未提供确认的验证器将面临惩罚。有关验证器惩罚的更多信息,请参阅 valset slashing。
ERC-20代币通过以下机制从以太坊转移到Injective:
将ERC-20代币存入Peggy合约:用户通过调用Peggy合约中的sendToInjective
函数发起将ERC-20代币从以太坊转移到Injective的操作,向Peggy合约存入代币并触发SendToInjectiveEvent
事件。存入的代币将保持锁定状态,直到在未来某个不确定的时刻被提取。该事件包含代币的数量和类型,以及接收资金的Injective链上的目标地址。
确认存款:每个Oracle见证SendToInjectiveEvent
并发送MsgDepositClaim
,该消息包含存款信息,发送到Peggy模块。
在Injective上铸造代币:一旦大多数验证者确认存款声明,存款将被处理。
如果资产来源于以太坊,代币将在Injective链上被铸造,并转移到目标接收者的地址。
如果资产来源于Cosmos-SDK,代币将被解锁并转移到目标接收者的地址。
请求从Injective提取:用户可以通过向Peggy模块发送MsgSendToEth
交易来发起从Injective链到以太坊的资产转移。
如果资产是以太坊原生资产,表示的代币将被销毁。
如果资产是Cosmos SDK原生资产,代币将在模块中被锁定。随后,提取请求将被加入到Outgoing Tx Pool
中。
批次创建:批次创建者观察待处理的提取池。批次创建者(或任何外部第三方)通过向Injective链发送MsgRequestBatch
请求为指定的代币创建批次。Peggy模块将匹配的代币类型的提取请求收集到一个批次中,并将其放入Outgoing Batch Pool
。
批次确认:当检测到存在Outgoing Batch
时,签名者使用其以太坊密钥对该批次进行签名,并向Peggy模块提交MsgConfirmBatch
交易。
将批次提交到Peggy合约:一旦大多数验证者确认了该批次,转发者将调用Peggy合约上的submitBatch
函数,提交该批次及其确认信息。Peggy合约验证签名,更新批次检查点,处理批次中的ERC-20提取请求,将批次费用转移到交易发送者,并触发TransactionBatchExecutedEvent
事件。
发送提取声明到Injective:Oracles见证TransactionBatchExecutedEvent
事件,并向Peggy模块发送包含提取信息的MsgWithdrawClaim
。
修剪批次:一旦大多数验证者提交了他们的MsgWithdrawClaim
,该批次将被删除,所有之前的批次也将被取消。取消的批次中的提取请求将被移回Outgoing Tx Pool
。
批次处罚:验证者负责确认批次,如果未能完成此操作,将受到处罚。有关批次处罚的更多信息,请参阅批次处罚。
注意:虽然批次处理显著降低了单次提取的费用,但这也带来了延迟和实现复杂度。如果用户希望快速提取,他们将需要支付更高的费用。然而,这个费用将大致与非批次系统中每次提取所需的费用相同。
该文档列出了Peggy模块读取/写入其状态的所有数据,以键值对(KV对)的形式。
Params
是一个模块范围的配置结构,存储参数并定义Peggy模块的整体功能。每个参数的详细规范可以在Parameters部分找到。
[]byte{0x4}
Module params
types.Params
Protobuf encoded
存储由验证者账户地址索引的委托以太坊地址。
[]byte{0x1} + []byte(validatorAddr)
Ethereum address
common.Address
Protobuf encoded
存储由委托以太坊地址索引的验证者账户地址。
[]byte{0xfb} + []byte(ethAddress)
Validator address
sdk.ValAddress
Protobuf encoded
当验证者希望将其投票权委托给另一个密钥时,值使用协调器地址作为键进行存储。
[]byte{0xe8} + []byte(AccAddress)
Orchestrator address assigned by a validator
[]byte
Protobuf encoded
这是桥接的验证者集合。由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
最新的验证者集合处罚计数器。用于跟踪需要被处罚的验证者集合以及已经被处罚的验证者集合。
[]byte{0xf5}
Nonce
uint64
encoded via big endian
最新验证者集合的计数器。每次新的验证者集合更新时,都会更新该计数器。
[]byte{0xf6}
Nonce
uint64
encoded via big endian
特定验证者集合的签名者确认。请参阅Oracle消息。
[]byte{0x3} + (nonce + []byte(AccAddress)
Validator Confirmation
types.MsgValsetConfirm
Protobuf encoded
特定代币批次的签名者确认。请参阅Oracle消息。
[]byte{0xe1} + []byte(tokenContract) + nonce + []byte(AccAddress)
Validator Batch Confirmation
types.MsgConfirmBatch
Protobuf encoded
用户的提取请求被汇集到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
每个由Injective接收到的提取请求的单调递增值。
[]byte{0x6} + []byte("lastTxPoolId")
Last used withdrawal ID
uint64
Big endian encoded
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
每个由批次创建者在Injective上创建的批次的单调递增值。
[]byte{0x7} + []byte("lastBatchId")
Last used batch ID
uint64
Big endian encoded
表示最新的处罚区块高度。始终只存储一个值。
[]byte{0xf7}
Latest height a batch slashing occurred
uint64
Big endian encoded
表示验证者开始从验证者集合中解除绑定的最新区块高度。用于确定处罚条件。
[]byte{0xf8}
Latest height at which a Validator started unbonding
uint64
Big endian encoded
一个来自计数链的代币标识符将来自一个合约。代币合约和代币标识符以两种方式存储。 首先,代币标识符作为键,值为代币合约。 其次,合约作为键,值为代币合约所代表的代币标识符。
[]byte{0xf3} + []byte(denom)
Token contract address
[]byte
stored in byte format
[]byte{0xf4} + []byte(tokenContract)
Token denom
[]byte
stored in byte format
此条目表示最后一次成功中继到以太坊的验证者集合(Valset)。在ValsetUpdatedEvent
的确认在Injective上处理后进行更新。
[]byte{0xfa}
Last observed Valset on Ethereum
types.Valset
Protobuf encoded
这是在以太坊上最后一次观察到的事件的计数器。当调用TryAttestation()
时设置此值。此存储中始终只持有一个值。
[]byte{0xf2}
Last observed event nonce
uint64
Big endian encoded
这是在以太坊上最后一次观察到的事件的区块高度。此存储中始终只会存储一个值。
[]byte{0xf9}
Last observed Ethereum Height
uint64
Protobuf encoded
这是来自特定验证者的最后一个观察到的以太坊事件。每次关联的协调器发送事件声明时都会更新。
type LastClaimEvent struct {
EthereumEventNonce uint64
EthereumEventHeight uint64
}
[]byte{0xf1} + []byte(validator address)
Last observed event by some Validator
types.LastClaimEvent
Protobuf encoded
确认是多个声明的汇总,随着更多的投票(声明)到来,最终被所有协调器观察到。一旦被观察到,该声明的特定逻辑将被执行。 每个确认都绑定到一个唯一的事件计数器(由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
一个计算出的哈希值,表明某个验证者集合/代币批次在Injective上确实存在。此检查点也存在于Peggy合约中。在每次新的验证者集合更新和代币批次创建时都会更新。
[]byte{0x1b}
Last created checkpoint hash on Injective
gethcommon.Hash
store in byte format
已知的恶意以太坊地址列表,这些地址被禁止使用桥接。
[]byte{0x1c} + []byte(ethereum address)
Empty []byte slice
gethcommon.Hash
stored in byte format]
这是Peggy消息类型的参考文档。有关代码参考和精确参数,请参阅proto定义。
这些是最终用户通过Injective链Peggy模块发送的消息。有关整个存款和提取过程的更详细概述,请参阅工作流程。
当用户希望将资金提取回以太坊时,发送到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
}
此消息允许用户取消尚未批量处理的特定提取请求。用户余额将被退还(金额 + 桥接费用)。
type MsgCancelSendToEth struct {
TransactionId uint64 // unique tx nonce of the withdrawal
Sender string // original sender of the withdrawal
}
此调用允许任何人提交证据,证明某个验证者签署了一个从未存在的验证者集合或批次。主体包含该批次或验证者集合。
type MsgSubmitBadSignatureEvidence struct {
Subject *types1.Any
Signature string
Sender string
}
这些消息由Peggy的批次创建子过程发送。
每当某个批次创建者发现汇集的提取请求,且在批量处理后满足其最低批次费用(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
}
这些消息由Peggy的Oracle子过程发送。
当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
}
当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
}
当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
}
当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
}
这些消息由Peggy的签名者子过程发送。
当签名者发现一个批次尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到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
}
当签名者发现一个验证者集合更新尚未由协调器(验证者)签署时,它会使用其委托的以太坊密钥构造签名,并将确认信息发送到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
}
转发者不会向Injective发送任何消息,而是构造包含Injective数据的以太坊交易,通过submitBatch
和updateValset
方法更新Peggy合约。
这些是直接使用验证者的消息密钥发送的消息。
由管理验证者节点的操作员发送到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中使用的各种削减条件。
该惩罚条件旨在阻止验证者对从未存在于 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% 的多数同意,控制多数的一方可能会直接拒绝提交证据。该惩罚条件最常见的应用场景是破坏试图控制桥接的验证者联盟,通过增加他们之间的信任难度,使其难以协调实施此类盗窃行为。
盗窃行为将涉及交换可惩罚的以太坊签名,这为联盟中的任何叛变者手动提交此类消息提供了可能性。
目前,该功能在状态中实现为一个不断增长的哈希数组。
当验证者在交易批次(transaction batch)由 Peggy 模块创建后的 SignedBatchesWindow
时间窗口内未能对其进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:
验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。
验证者联盟拒绝签名 超过 1/3 的验证者可能解除绑定(unbond)并拒绝签署更新,从而导致任何交易批次都无法获得足够的签名以提交至 Peggy 以太坊合约。
当验证者未能对由 Peggy 模块生成的验证者集合更新(validator set update)进行签名时,将触发该惩罚条件。此机制旨在防止以下两种不良情况的发生:
验证者未运行正确的二进制文件 验证者可能未在其系统中运行正确的二进制文件,导致其无法履行签名职责。
验证者联盟拒绝签名 超过 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 的惩罚风险。
以太坊预言机代码(目前主要集中在 attestation.go
文件中)是 Peggy 模块的关键组成部分。它使 Peggy 模块能够了解以太坊上发生的事件,例如存款(deposits)和已执行的交易批次(executed batches)。PEGGYSLASH-03 旨在惩罚那些提交了从未在以太坊上发生的事件的声明的验证者。
我们判断一个事件是否在以太坊上发生的唯一方式是通过以太坊事件预言机本身。因此,为了实现此惩罚条件,我们会惩罚那些在与 >2/3 验证者观察到的事件具有相同随机数(nonce)的情况下提交了不同事件声明的验证者。
尽管初衷良好,但该惩罚条件可能不适用于大多数 Peggy 应用场景。这是因为它将安装 Peggy 的 Injective Chain 的正常运行与以太坊链的正常运行绑定在一起。如果以太坊链发生严重分叉,不同验证者可能会在同一事件随机数下看到不同的事件,并因此受到惩罚,尽管他们并无过错。大规模的不公平惩罚将对 Injective Chain 的社会结构造成严重破坏。
该惩罚条件的实际效用在于,如果 >2/3 的验证者形成一个联盟,在某个随机数下提交虚假事件声明,联盟中的部分成员可能会叛变并在同一随机数下提交真实事件声明。如果有足够多的联盟成员叛变,使得真实事件被观察到,那么剩余的联盟成员将因此惩罚条件受到惩罚。然而,这通常需要超过一半的联盟成员叛变。
如果没有足够多的联盟成员叛变,则两个事件都不会被观察到,以太坊预言机将停止运行。这种情况比 PEGGYSLASH-04 实际被触发的场景更为可能。
此外,在联盟成功的情况下,PEGGYSLASH-04 可能会针对诚实验证者触发。这可能使正在形成的联盟更容易威胁那些不愿加入的验证者。
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 可能因以太坊生成的因素而完全停止的可能性。
在每个区块结束时,将对模块的状态执行以下操作:
当验证者未能对通过 SignedValsetsWindow
的验证者集合更新(valset update)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为验证者集合更新提供确认,他们将因其抵押的 SlashFractionValset
部分被罚没,并立即被监禁(jailed)。
当验证者未能对通过 SignedBatchesWindow
的交易批次(batch)进行签名时,将受到惩罚。换句话说,如果验证者未能在预配置的时间内为交易批次提供确认,他们将因其抵押的 SlashFractionBatch
部分被罚没,并立即被监禁(jailed)。
任何仍在 Outgoing Batch 池中且其 BatchTimeout
(指定的以太坊区块高度,交易批次应在此高度之前执行)已超时的交易批次将被从池中移除,相关的提款请求将重新插入到 Outgoing Tx 池中。
在以下情况下,将自动创建新的验证者集合更新(Validator Set update):
验证者权力变化超过 5%:当最新验证者集合与当前验证者集合之间的权力差异(power diff)超过 5% 时。
验证者开始解绑:当某个验证者开始解绑(unbonding)时。
新的验证者集合最终将被中继(relayed)至以太坊上的 Peggy 合约。
已通过 SignedValsetsWindow
的先前观察到的验证者集合(valsets)将从状态中移除。
处理当前正在投票的所有证明(attestations)(针对特定事件的声明集合)。每个证明依次处理,以确保每个 Peggy 合约事件都被处理。在处理完每个证明后,模块的 lastObservedEventNonce
和 lastObservedEthereumBlockHeight
将被更新。
根据证明中的声明类型,执行以下操作:
MsgDepositClaim:为接收者地址铸造/解锁存入的代币。
MsgWithdrawClaim:从 Outgoing 池中移除相应的交易批次,并取消任何先前的批次。
MsgValsetUpdatedClaim:更新模块的 LastObservedValset
。
MsgERC20DeployedClaim:验证新的代币元数据并将其注册到模块的状态中(denom <-> token_contract)。
之前处理过的证明(高度早于 lastObservedEthereumBlockHeight
)会从模块状态中移除。
peggy
模块会触发以下事件:
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}
string
reason
{reason_for_slashing}
int64
power
{validator_power}
string
consensus_address
{consensus_addr}
string
operator_address
{operator_addr}
string
moniker
{validator_moniker}
string
validator_address
{validator_addr}
string
orchestrator_address
{orchestrator_addr}
string
operator_eth_address
{eth_addr}
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}
withdrawal_cancelled
bridge_contract
{bridge_contract}
withdrawal_cancelled
bridge_chain_id
{bridge_chain_id}
string
denom
{token_denom}
string
orchestrator_address
{orch_addr}
uint64
batch_nonce
{batch_nonce}
uint64
batch_timeout
{block_height}
[]uint64
batch_tx_ids
{ids}
string
bridge_contract
{bridge_contract}
uint64
bridge_chain_id
{bridge_chain_id}
uint64
batch_id
{id}
uint64
nonce
{nonce}
uint64
valset_nonce
{nonce}
string
orchestrator_address
{prch_addr}
uint64
batch_nonce
{nonce}
string
orchestrator_address
{orch_addr}
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}
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}
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}
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 客户端实现用于一致性检查。
signed_valsets_window
signed_batches_window
signed_claims_window
这些值表示验证者必须在多少个区块时间内提交批次或 valset 的签名,或者提交特定证明 nonce 的声明。 在证明的情况下,这个时钟从证明创建时开始,但仅在事件经过后才允许进行惩罚。请注意,声明惩罚当前尚未启用,详见惩罚规范。
target_batch_timeout
这是批次超时的“目标”值,之所以称为目标,是因为 Ethereum 是一个概率链,无法提前确定区块频率。
average_block_time
average_ethereum_block_time
这些值分别是 Injective Chain 和 Ethereum 的平均区块时间,用于计算目标批次超时。若区块生成时间发生任何重大且长期的变化,治理需要更新这些值。
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
,然后该批次会被创建,供中继者中继。
对于现有的批次,中继者也应该评估其盈利性,并使用类似的方法尝试中继。
验证者在 Peggy Orchestrator 中运行所需的 Eth Signer,因为目前我们无法在不对 Tendermint 进行重大修改的情况下,将这种简单的签名逻辑插入到基于 Cosmos SDK 的链中。未来,随着对 Tendermint 的修改,这可能成为可能。
需要注意的是,如果能够在共识代码中执行 Ethereum 签名,则可以消除 PEGGYSLASH-02
和 PEGGYSLASH-03
,而不会损失安全性。这是一个相对有限的功能增强,可以使 Peggy 更不容易受到惩罚。
目前,Peggy 中的验证者只有一个正向激励——桥接正常运行所带来的额外活动对链的促进。 另一方面,验证者必须注意许多负向激励(惩罚)。这些在惩罚规范中有所概述。 一个未在惩罚中涵盖的负向激励是提交预言机提交和签名的成本。目前,这些操作没有激励,但验证者仍然需要支付费用进行提交。考虑到当前 Injective Chain 上相对便宜的交易费用,这不是一个严重的问题,但随着交易费用的上涨,这无疑是一个需要考虑的重要因素。 对于正确参与桥接操作的验证者,应该考虑一些正向激励措施,除了消除强制性提交的费用之外。