本文深入探讨了Anoma协议的架构与工作原理,介绍了其独特的意图(intents)概念、匹配机制(matchmakers)、状态机设计、有效性谓词(validity predicates)和隐私特性等,展示了Anoma如何在众多协议中实现去中心化的事务协调和执行,同时维持安全性和用户隐私。文章不仅详细阐述了技术实现,还提供代码示例,适合有一定基础的读者。
Anoma的分形视角
关于Anoma的最新文章,请访问 anoma.net/blog
最近,我与许多初识Anoma的人聊天,他们希望了解更多关于该项目的信息。在大多数情况下,我觉得有必要首先对架构做一个快速概述,然后再深入到示例应用或用例中。我的目标是使本文作为参考,以回答以下问题:“Anoma是如何工作的?”、“它与其他协议的根本区别是什么?”或“我在Anoma上能做什么是其他协议无法做到的?”
Anoma协议被设计为一种通用的协调机制,能够促进各种形式的协调,无论它们是多么简单或复杂,涉及的参与者数量是几个还是很多(从1到n
)。由于Anoma协议的架构在许多方面都是前所未有的,本文提供了对架构的易读概述,并解释了为何它是以这种方式设计的。
在Anoma中,有一个概念叫做意图。你可以将意图视为部分或未最终确认的交易,可以由任何人创建。它们是由用户精心制作和签名的消息,表达了特定的愿望或偏好。意图可以是任何东西,简单的可读信息、一系列特征或约束,甚至是复杂的程序。因此,意图能够传递经济价值,并构成约束性偏好表达,用户承诺在特定情况下采取特定行动(同意状态变更)。
以下是将Albert、Bertha和Christel的示例意图写入文件的命令:
echo ‘[{“addr”:”’$ALBERT’”,”key”:”’$ALBERT’”, “max_sell”:”10",”min_buy”:”1", “buy”:”’$veal’”,”sell”:”’$potato’”}]’> intent.A.data
echo ‘[{“addr”:”’$BERTHA’”,”key”:”’$BERTHA’”,”max_sell”:”1",”min_buy”:”5", “token_buy”:”’$apple’”,”token_sell”:”’$veal’”}]’ > intent.B.data
echo '[{"addr":"'$CHRISTEL'","key":"'$CHRISTEL'","max_sell":"5","min_buy":"10", "token_buy":"'$potato'","token_sell":"'$apple'"}]' > intent.C.data
请注意,意图只是签名消息,最终用户可以通过不同接口创建它们,从命令行工具到网络或本地用户界面。
一旦意图被制作出来,就可以提交到意图传播节点,这是Anoma的点对点层的一个特殊组成部分。意图传播节点可以由任何人操作,包括制作意图的最终用户。意图传播节点的操作人员形成意图池(可能没有全球意图池,因为那需要所有传播节点操作员之间的完美同步),其中包含用户提交给至少一个传播节点的任何意图。意图传播节点协议的设计可能包括一项费用或奖励共享机制,所有参与某意图传播并随后成功结算的传播节点都可.claim这些奖励——这可以定义为意图传播标准,但最终是由意图传播操作员(或希望将其包含在意图中的用户)定义的。
匹配者是网络上的另一个独特角色。他们主动监控意图池,目的是尽可能多地找到意图,使其综合后能够满足每个意图中表达的偏好。当匹配者的角色更为具体时,可以将非常基本的匹配者想象成一个手动检查每个意图并通过心算确定哪些可以组合的人。简单地将上述示例意图翻译成文字:
请注意,没有意图具有直接匹配或意愿的双重重合。但优秀的匹配者可以发现这三个特定意图在组合时能够互相满足。
匹配者的方法的复杂性没有限制:他们可以是部署专有优化算法的协调小组,在物理优势的地点操作专业硬件基础设施,或者甚至可以是某种形式的人工智能——毕竟,匹配者是在相互竞争,可以做任何事情来获取优势。
当一组可以组合的意图被找到后,匹配者将它们聚合并构建一个交易,可以提交给任何全节点。
以下是一个用Rust实现的非常基本的匹配者程序示例:
/// 尝试在图中找到匹配的意图。如果找到,则返回交易字节
/// 和匹配意图ID的哈希集合。
fn try_match(
graph: &mut DiGraph<ExchangeNode, Address>,
) -> Option<(Vec<u8>, HashSet<Vec<u8>>)> {
// 我们只使用第一个找到的环,因为一个意图无法在多个交易中匹配
if let Some(mut matchned_intents_indices) =
petgraph::algo::tarjan_scc(&*graph).into_iter().next()
{
// 一个节点与自身是一个环
if matchned_intents_indices.len() > 1 {
println!("found a match: {:?}", matchned_intents_indices);
// 须按照逆序排序,因为按索引移除节点,否则将无法移除正确的节点
matchned_intents_indices.sort_by(|a, b| b.cmp(a));
if let Some(tx_data) =
prepare_tx_data(graph, &matchned_intents_indices)
{
let removed_intent_ids = matchned_intents_indices
.into_iter()
.filter_map(|i| {
if let Some(removed) = graph.remove_node(i) {
Some(removed.id)
} else {
None
}
})
.collect();
return Some((tx_data, removed_intent_ids));
}
}
}
None
}
可以在这里找到完整的实现:
anoma/lib.rs at master · anoma/anoma \ \ 此文件包含双向Unicode文本,其解释或编译可能与以下内容有所不同…\ \ github.com
与大多数现有协议的架构不同,在Anoma中,交易的应用是逐步进行的,但交易的验证是在事后完成的,并且不是在执行过程中交替进行的。换句话说,状态转变的验证与逐步执行是解耦的,因此是无状态的,这使得容易并行化。通过部署有效性谓词(VPs)来促进状态转变的保障和正确性。
你可以将VPs视为用户账户的不可变性。传统上,不可变性的概念已经在核心协议中实现,例如在某种加密货币的总供应上限。在Anoma上,每个用户账户都有基本的VP,涵盖安全约束,例如仅允许对应私钥的签名授权支出,账户拥有足够的余额以支出特定金额,某个密钥可以更新VP等。这使得两个非常有趣的事情得以实现:
其中一个源于状态机只有在没有所有交易中涉及的所有动作和交互在一个区块中相互冲突或违反任何被更改账户的VP规则时,才会允许状态转变通过(然后被然后记录到账本上)。
第二个有趣的性质是,有效性谓词可以是任何东西:它们可以是一组简单的逻辑约束,可以是函数,甚至可以是整个算法。最重要的是,有效性谓词验证状态更改,并且它们不需要执行后者的计算,因此,用户可以涉及任意数量的NP困难状态更改。
以下是用Rust实现的基本用户账户有效性谓词示例:
[...]#[validity_predicate]
fn validate_tx(
tx_data: Vec<u8>,
addr: Address,
keys_changed: HashSet<storage::Key>,
verifiers: HashSet<Address>,
) -> bool {
debug_log!(
"vp_user called with user addr: {}, key_changed: {:?}, verifiers: {:?}",
addr,
keys_changed,
verifiers
);
let signed_tx_data =
Lazy::new(|| SignedTxData::try_from_slice(&tx_data[..]));
let valid_sig = Lazy::new(|| match &*signed_tx_data {
Ok(signed_tx_data) => {
let pk = key::get(&addr);
match pk {
Some(pk) => verify_tx_signature(&pk, &signed_tx_data.sig),
None => false,
}
}
_ => false,
});
[...]
}
有趣的是,你可以将有效性谓词视为持久的偏好表达(尽管它们可以随时更新),而意图则是短暂的偏好表达。
例如,如果用户Alice只想进行包含碳成本的交互,Alice可以以使得只有满足该要求的事务有效的方式自定义其账户的VP。然而,如果Alice只是想一次性购买包含碳成本的特定商品,那么她可以制定一个设置该特征的意图。
使验证和计算拆分的架构含义明显不仅限于效率和增强的用户体验。实际上,有效性谓词的概念使得可以尽可能地将逻辑与协议的核心架构分离,以至于许多特性和属性都作为VP实现。这些VP具有对协议不变性的特殊访问权限。
实现协议范围特性的一些特殊VP示例是IBC集成、PoS、治理、代币(例如原生代币)或诸如通过多资产隐匿池(MASP)作为VP的隐私特征。
以下是用Rust实现的简单治理机制VP的示例:
[...]pub fn proposal_vp(tx_data: Vec<u8>, addr: Address, keys_changed: HashSet<Key>, verifiers: HashSet<Address>) {
for key in keys_changed {
if is_vote_key(key) {
return (is_delegator(verifiers) || (is_validator(verifiers) && current_epoch_is_2_3(addr, id))) && is_valid_signature(tx_data);
} else if is_content_key(key) {
let post_content = read_post(key)
return !has_pre(key) && post_content.len() < MAX_PROPOSAL_CONTENT_SIZE;
} else if is_author_key(key) {
return !has_pre(key)
} else if is_proposa_code_key(key) {
let proposal_code_size = get_proposal_code_size();
return !has_pre(key) && proposal_code_size < MAX_PROPOSAL_CODE_SIZE;
} else if is_grace_epoch_key(key) {
let endEpoch = read_post_end_epoch(addr, id, END_EPOCH_KEY)
let graceEpoch = read_post_grace_epoch(addr, id, GRACE_EPOCH_KEY)
return !has_pre(key) && graceEpoch > endEpoch;
} else if is_balance_key(key) {
let pre_funds = read_funds_pre(key)
let post_funds = read_funds_post(key)
let minFunds = read_min_funds_parameter()
return post_funds - pre_funds >= MIN_PROPOSAL_FUND;
} else if is_start_or_end_epoch_key(key) {
let id = get_id_from_epoch_key(key);
let currentEpoch = read_current_epoch();
let minPeriod = read_min_period_parameter();
let startEpoch = read_post_start_epoch(addr, id, START_EPOCH_KEY);
let endEpoch = read_post_end_epoch(addr, id, END_EPOCH_KEY)
return !has_pre(key) && startEpoch - currentEpoch >= MIN_PROPOSAL_PERIOD && (startEpoch - currentEpoch) % MIN_PROPOSAL_PERIOD == 0;
} else if is_param_key(key) {
let proposal_id = get_proposal_id();
return is_tally_positive(proposal_id);
}
} else {
return false;
}
}
}[...]
你可以在这里找到完整实现版本:
spec/governance.md at governance · anoma/spec \ \ Anoma引入一种治理机制以提议和应用协议更改,无需进行硬分叉…\ \ github.com
Anoma的抗Sybil机制是基于股权证明和立方体削减,旨在打击操作多个状态相同验证节点的验证者。与线性削减不同,线性削减会使验证者削减固定比例的资金,立方体削减的最低比例为1%,但如果三分之一或更多的投票权在短时间内出现故障,将变为100%。以下是立方体削减的原型:
function calculateSlashRate(slashes) {
votingPowerFraction = 0
for slash in slashes:
votingPowerFraction += slash.validator.votingPowerFraction
return max(0.01, min(1, votingPowerFraction**2 * 9)) // 最小削减率为1%
// 然后在0与1/3投票权之间采用指数
}
在早期版本中,权益奖励机制遵循一种改进版的F1费用分配设计,以最小化与抵押、提取奖励和重抵押奖励相关的交互数量。相反,奖励将自动复利,而用户仍然可以更改特定参数,例如验证者。
协议的初始版本部署了Tendermint,一种拜占庭容错(BFT)共识机制。后来的版本部署了更高级的跨链共识,特别是异构Paxos。在异构链的状态之间共享共识的能力特别有趣,因为Anoma通过分形实例进行扩展。
在Anoma生态系统的较成熟阶段,有多个主权链(分形实例),提供不同的功能(例如,一个提供多资产隐私,另一个作为结算层,另一个作为交易协议等),在协议中具有不同的特性(PoW、PoA、PoS或任何其他形式的抗Sybil机制),或者是在地理上局限的版本(例如,由位于德国的验证者操作的结算层,由驻扎在柏林的验证者操作的另一个版本,以及由驻扎在克罗伊茨贝格的团队操作的另一个版本)。
下图描绘了Anoma的世界观,以许多Anoma协议的形式共存为特征(它们关注不同的功能,由不同几何形状表示)和许多同类的实例(分形实例),运营参与者的规模任意:
Anoma的世界观,由许多形式的协议(不同形状)和许多相同形式的分形实例共存
相同形式的分形实例可能相互连接(有色箭头),并与不同形式连接(下面的深灰色箭头):
在Anoma的世界观中,许多形式和分形实例可能相互连接到同类和/或不同实例
下图描绘了多个分形实例的共存,这些实例专注于为交易启用结算层:
一种Anoma的形式,从全球实例开始并连接到一些本地实例的结算层
这种扩展方式最大限度地实现了构建者、运营者和最终用户之间的激励重叠,并实现了各实例之间尽可能多的架构灵活性。例如,柏林、德国和欧盟的实例操作员可以决定互相操作,因为有机经济相互依赖,而克罗伊茨贝格社区的实例可以决定只与全球实例和特定实例(例如Full Node的实例,欢迎来访我们位于柏林的团队!)进行互通。
除了互操作性,分形实例还可以部署不同的治理结构,例如,如果Full Node是一个合作社,则在PoS中的奖励分配机制和基于交易结算的费用分配可以在合作社的所有成员中平均分配,或者他们可以决定将后一部分分配给专门用于进一步地方公共商品的国库,例如Görlitzer公园。
Anoma的设计理念是,所有用户与点对点层和状态机的功能交互都可以在保持完全零知识隐私的情况下进行——这取决于用户的偏好。
Anoma早期的分形实例专注于启用资产无关的遮蔽转账,利用MASP电路,使得所有类型的资产,包括可替代和不可替代资产,以及从本地到非本地资产(通过自定义桥接或IBC集成进行传输)都能共享同一匿名集。由于匿名集是一个集体商品,使用越多,对每个人的隐私保障就越强,无论他们的偏好是什么(有些人可能不在乎隐私),这个Anoma的分形实例部署一个方案来激励遮蔽池的使用。为同时启用多资产遮蔽转账和遮蔽池用户的激励,需定制电路(见烧与铸)。
在未来的Anoma分形实例中,旨在启用更复杂形式的协调,例如n方交易或以物易物,常常需要某些数据是透明的,以履行计算。例如,在上述三方以物易物的例子中,某些信息比如什么($potato、$veal、$apple)必须是透明的,而谁可以保持私密。为了支持隐私保护的以物易物以及未来的协调用例,Anoma的架构支持Taiga,这是一个通用的私有状态转变框架,支持用户与应用状态,与意图传播和匹配层集成。
与Anoma旨在实现自我主权协调的愿景一致,Anoma生态系统包含适合承载隐私保护应用的平台无关开发工具,如Plonkup、Vamp-IR和Juvix。
Plonkup 是最可配置的证明系统,让开发者能够编写任何类型的SNARK,并且无需可信设置、递归、累积方案等;实现是与其他ZK-Garage成员合作完成的。
Vamp-ir 是一个与证明系统无关的中间表示,可以目标任何编程语言(包含Juvix)。同时,Vamp-IR可以编译为任何电路后端,包括R1CS,普通Plonk,带查找的Plonk,具有自定义门的Plonk,或任何其他约束系统。
Juvix 是一种依赖类型的编程语言,用于编写后端和证明系统无关的零知识应用程序,包括Anoma上的有效性谓词。
你读到最后了吗?哇,感谢你的阅读!我希望这篇文章不仅能够回答你对Anoma如何工作的疑问,还能帮助你理解它的用例及其独特的应用。现在我们已经对架构有了一个概述,期待能以更多的文章激励你,描述Anoma所特有架构可以启用的一些最有趣的用例。
关于每个组件的进一步探索:
- 原文链接: medium.com/anomanetwork/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!