前言
polygon-edge 源码版本:v0.6.3;
core-contracts 源码版本:pre-alpha
Poly Edge 的虚拟机兼容以太坊的EVM(采用与以太坊完全相同的指令集),可以完美使用以太坊的生态,尤其是solidty合约,以及以太坊生态的相关工具;Poly Edge 是基于插件体系的模块化建构,不是以fork 以太坊的方式,但实现了对以太坊链的兼容。
1. 架构概述
基于插件体系的模块化建构
1)libp2p:为 Supernets
提供网络层,是点对点的网络架构设计;
2)Bridge:由 PolyBFT
启用的内置桥接机制,允许在 Supernet
和另一个权益证明区块链之间传递消息,而无需映射;
3)Mempool:使多个验证节点能够聚合它们的签名,用来创建代表内存池中所有验证节点的单个聚合签名;
4)Consensus:PolyBFT
作为 Polygon Supernets
的共识机制,由一个共识引擎 IBFT 2.0
和一个包括 bridge
、staking
和其他实用程序的共识协议组成;
5)Blockchain:协调系统中的一切,管理世界状态转换,并在新块添加到链中时负责状态更改;
6)Runtime (EVM):使用 EVM
作为执行智能合约的运行时环境;
7)TxPool:表示交易池,与系统中的其他模块紧密相连;
8)JSON-RPC:促进 dApp
开发人员与区块链之间的交互,允许开发人员向Supernets
节点发出 JSON-RPC
请求并接收响应;
9)gRPC:对于操作员交互至关重要,允许节点操作员轻松地与客户端交互,并提供无缝的用户体验。
下图说明了 Supernets
的主要组成部分。
2. 数据结构
在 polygon-edge
中,区块和交易是重要的两个数据结构。
2.1 Block
type Block struct {
Header *Header
Transactions []*Transaction
Uncles []*Header // 叔块
// Cache
size atomic.Value // *uint64
}
// Header represents a block header in the Ethereum blockchain.
type Header struct {
ParentHash Hash
Sha3Uncles Hash
Miner []byte // 矿工
StateRoot Hash
TxRoot Hash
ReceiptsRoot Hash
LogsBloom Bloom // 事件地址和事件 topic 的布隆滤波器
Difficulty uint64
Number uint64
GasLimit uint64
GasUsed uint64
Timestamp uint64
ExtraData []byte
MixHash Hash
Nonce Nonce
Hash Hash
}
polygon-edge
目前不支持以太坊最新的 EIP-1599
协议,其它部分和 ethereum
完全一致(虽然变量名称有些有区别,但数据类型完全一致,甚至映射的 json 名称也完全一致)。
2.2 Transaction
type Transaction struct {
Nonce uint64
GasPrice *big.Int
Gas uint64
To *Address
// 如果此交易用于发送和接收以太币,这里是接收账户中计算的Wei代币数量;
// 如果此交易用于向合约发送消息调用,则这里是支付给接收此消息的智能合约的Wei金额;
// 如果此交易用于创建合约,则此处为初始化合约时存储在账户中的Wei以太币数量。
Value *big.Int
Input []byte
V *big.Int // V、R、S:加密签名中使用的值,可用于确定交易的发送方
R *big.Int
S *big.Int
Hash Hash
From Address
// Cache
size atomic.Value
}
从关键的block和transaction的数据结构来看,polygon-edge
的数据结构基本上遵循与ethereum
的数据结构一致的设计,他们稍有不同的地方,但不影响与传统ethereum链的跨链数据交换。
2.3 链结构
3. 常见问题
3.1 怎么区分POS网络和Supernet网络?
Polygon PoS
链只是一种短期临时的扩容解决方案,它的代价是牺牲了安全性和去中心化,母链是Ethereum
;Supernet
使用共识客户端Edge
搭建多节点网络Polygon PoS
链仅使用其EVM
合约在PoS
链上保存带有交易数据的Checkpoints
(检查点),验证检查点的代码如下(core-contracts/contracts/root/CheckpointManager.sol:209):
/**
* @notice Internal function that performs checks on the checkpoint
* @param prevId Current checkpoint ID
* @param checkpoint The checkpoint to store
*/
function _verifyCheckpoint(uint256 prevId, Checkpoint calldata checkpoint) private view {
Checkpoint memory oldCheckpoint = checkpoints[prevId];
require(
checkpoint.epoch == oldCheckpoint.epoch || checkpoint.epoch == (oldCheckpoint.epoch + 1),
"INVALID_EPOCH"
);
// 检查点的区块高度必须大于先前的区块高度
require(checkpoint.blockNumber > oldCheckpoint.blockNumber, "EMPTY_CHECKPOINT");
}
3.2 公链的奖励机制是怎样的?
(1)交易费用奖励:在Polygon公链上进行交易需要支付一定数量的交易费用,这些交易费用将被用于奖励验证人和参与者。验证人将获得交易费用的一部分作为奖励,以鼓励他们验证交易的合法性。同时,参与者也可以获得一定数量的交易费用作为奖励,以鼓励他们参与网络的建设和使用。验证器和参与者获得奖励的算法见 core-contracts/contracts/child/validator/RewardPool.sol:41:
/**
* @inheritdoc IRewardPool
*/
function distributeRewardFor(uint256 epochId, Uptime[] calldata uptime) external onlySystemCall {
require(paidRewardPerEpoch[epochId] == 0, "REWARD_ALREADY_DISTRIBUTED");
// 获取指定POS协议的回合内的区块总数
uint256 totalBlocks = validatorSet.totalBlocks(epochId);
require(totalBlocks != 0, "EPOCH_NOT_COMMITTED");
uint256 epochSize = validatorSet.EPOCH_SIZE();
// slither-disable-next-line divide-before-multiply
// 计算基础奖励
uint256 reward = (baseReward * totalBlocks) / epochSize;
// 计算指定POS协议的回合内的总供应量
uint256 totalSupply = validatorSet.totalSupplyAt(epochId);
uint256 length = uptime.length;
uint256 totalReward = 0;
for (uint256 i = 0; i < length; i++) {
Uptime memory data = uptime[i];
require(data.signedBlocks <= totalBlocks, "SIGNED_BLOCKS_EXCEEDS_TOTAL");
// slither-disable-next-line calls-loop
// 获取指定回合内的验证者的余额
uint256 balance = validatorSet.balanceOfAt(data.validator, epochId);
// slither-disable-next-line divide-before-multiply
// 计算签名奖励
uint256 validatorReward = (reward * balance * data.signedBlocks) / (totalSupply * totalBlocks);
pendingRewards[data.validator] += validatorReward;
totalReward += validatorReward;
}
// 保存给定回合内的总报酬
paidRewardPerEpoch[epochId] = totalReward;
// 此方法可以重写
// 转移总奖励
_transferRewards(totalReward);
emit RewardDistributed(epochId, totalReward);
}
(2)质押奖励:在Polygon公链上,用户可以将自己的代币质押到网络中,以支持网络的安全性和稳定性。质押代币的用户将获得一定数量的奖励,这些奖励将从交易费用中提取出来,并按照一定比例分配给质押者。
验证者需要抵押一定数量的MATIC代币才能参与网络的验证和出块,同时也会获得相应的奖励。这些奖励会从代币总量中自动发行,因此也会释放一定的MATIC代币。
验证者抵押代币的操作步骤见 polygon
质押。下面的代码是验证者质押代币(core-contracts/contracts/root/staking/StakeManager.sol:36):
function stakeFor(uint256 id, uint256 amount) external {
require(id != 0 && id <= _chains.counter, "INVALID_ID");
// slither-disable-next-line reentrancy-benign,reentrancy-events
// 将币质押给当前合约
matic.safeTransferFrom(msg.sender, address(this), amount);
// calling the library directly once fixes the coverage issue
// https://github.com/foundry-rs/foundry/issues/4854#issuecomment-1528897219
// 增加质押量
StakeManagerLib.addStake(_stakes, msg.sender, id, amount);
ISupernetManager manager = managerOf(id);
// 同步状态
manager.onStake(msg.sender, amount);
// slither-disable-next-line reentrancy-events
emit StakeAdded(id, msg.sender, amount);
}
(3)挖矿奖励:在Polygon公链上,用户还可以通过挖矿获得代币奖励。挖矿是一种参与网络的方式,它需要用户提供计算资源来验证交易和创建新的区块。挖矿奖励将从交易费用中提取出来,并按照一定比例分配给挖矿者。比例配置不是通过 config.go
设置的,而是通过合约设置的,见代码:core-contracts/contracts/interfaces/child/validator/IRewardPool.sol:23
struct Uptime {
address validator; // 挖矿者 || 验证者 || 参与者 || 质押者
uint256 signedBlocks; // 当前签名区块高度
}
/// @notice distributes reward for the given epoch
/// @dev transfers funds from sender to this contract
/// @param uptime uptime data for every validator
/// uptime 是一个数组
function distributeRewardFor(uint256 epochId, Uptime[] calldata uptime) external;
(4)社区奖励:Polygon公链还将设立社区奖励,以鼓励社区成员参与网络的建设和推广。社区奖励将从交易费用中提取出来,并按照一定比例分配给社区成员。
3.4 如何计算允许故障的验证器数量?
polygon-edge/consensus/ibft/validators.go:10
func CalcMaxFaultyNodes(s validators.Validators) int {
// N -> number of nodes in IBFT
// F -> number of faulty nodes
//
// N = 3F + 1
// => F = (N - 1) / 3
//
// IBFT tolerates 1 failure with 4 nodes
// 4 = 3 * 1 + 1
// To tolerate 2 failures, IBFT requires 7 nodes
// 7 = 3 * 2 + 1
// It should always take the floor of the result
// (验证器数量-1)/3
return (s.Len() - 1) / 3
}
3.5 如何计算提议节点(Proposer)?
polygon-edge/consensus/ibft/validators.go:70
func CalcProposer(
validators validators.Validators,
round uint64,
lastProposer types.Address,
) validators.Validator {
var seed uint64
// 在最后一个提议者地址不为空的情况下
if lastProposer == types.ZeroAddress {
seed = round
} else {
offset := int64(0)
// 取此地址在验证者集合中的位置
if index := validators.Index(lastProposer); index != -1 {
offset = index
}
// 加上当前共识轮数
seed = uint64(offset) + round + 1
}
// 对验证者集合的长度求余
pick := seed % uint64(validators.Len())
return validators.At(pick)
}
3.6 Polygon 是如何工作?
用以太坊第一层区块链作为基础区块链,是构建分布式应用程序的基础,含共识机制和原生代币。第二层解决方案建立在第一层网络之上,在基础层上形成第二层。这些第二层扩展解决方案通常依赖基础层来实现安全性和共识,第二层网络从主梁或父链中取出批量交易,对其进行处理,然后将汇总数据返回到主链。
我猜“第二层网络从主梁或父链中取出批量交易,并对其进行处理”,这段逻辑没有写在 polygon-edge
中,而是通过 eth-event-tracker 来处理 polygon
感兴趣的区块事件,然后通过链合约把汇总数据返回到主链。
3.7 如何在 Polygon 上建立自己的DApp?
DApp
是通过智能合约构建起来的,而在 polygon
上建立自己的 DApp
,就是在编写和部署智能合约。为了更容易开发 DApp
,我选择了 Truffle
中某个项目作为模块。
Truffle 是一个基于以太坊技术的开发、测试和部署框架,提供了很多项目模板,可以快速搭建一个 DApp
。建议在 Truffle
项目中选择一个合适的项目模板,开发自己的 DApp
。
(1)安装 Truffle
$npm install -g truffle
(2)创建一个项目
对于初学者来说,建议不要使用truffle init
初始化项目,因为该指令生成的项目是没有智能合约的裸项目,会缺乏 web 相关文件,无法更直观的看到开发效果。若要初始化 web
项目,可以使用
$truffle unbox webpack -- 构建一个基于webpack的项目
若要建立转移 Token
的项目,可以使用
$truffle unbox metacoin [PATH/TO/DIRECTORY]
(3)项目结构
app:项目的入口文件,里面包括了用于合约交互的 javascript,和用于显示前端的css等静态文件,只会在初始化 web 项目时生成;
contracts:合约代码的,默认会有三个合约
migrations:合约部署脚本、配置信息
test:测试应用程序和智能合约的测试文件目录
truffle.js:Truffle 项目主配置文件
(4)测试
若是基于模板初始化的项目,可以直接运行测试;若是通过 init
初始化的项目,则需要添加合适的合约和逻辑。要运行所有测试,可以使用:
$truffle test
更多关于测试信息,见文档
(5)编译合约
$truffle compile
(6)部署到网络
$truffle migrate --默认配置是部署到本地的区块链网络
4. 生态
4.1 DApp
Polygon 生态内项目达数千个,DeFi、NFT、游戏占据主导。
Polygon 为了鼓励生态项目发展,推出了其基于 KPI 的流动性挖矿,根据每周平均用户数或 TVL 等 KPI 数据,将奖励分配给符合标准的 DApp。
4.2 主要产品
(1)Polygon PoS (是 Layer 1 的侧链);
(2)Polygon zkEVM(是基于 ZK Rollup 技术搭建的以太坊 Layer2 层 zkEVM 扩容方案,ZK Rollup 是利用「零知识证明」验证方式来落实 Rollup 的扩容方案。Polygon zkEVM 上线后是一个单独运行的区块网络,Polygon POS 链继续作为侧链运作。);
(3)Polygon Supernets(允许创建特定用例的专用区块链框架);