# 欢迎

## Aptos 开发者网络

欢迎！Aptos 正在为每个人构建 1 层网络。 在我们的文档中，您可以探索 Aptos 网络的教程和技术概念。

<mark style="color:red;">**目前 Aptos 不需要筹集资金，没有任何私募、公募，请不要相信任何 私聊 / 空投 / 代币兑换网站！**</mark>

### 🤔 有疑问？ <a href="#got-a-question" id="got-a-question"></a>

加入我们的 Discord，与我们的开发人员交谈并加入 Aptos 社区。 这是了解 Aptos 最新消息和发展的最佳方式。请务必查看频道中的置顶消息 - 这是我们希望保留特定主题链接、事件等的地方。&#x20;

官方 Twitter：<https://twitter.com/aptoslabs>[​](https://aptos.dev/#-got-a-question)

官方 Discord：<https://discord.gg/aptoslabs>

官方 Medium：<https://medium.com/aptoslabs>

<mark style="background-color:green;">中文社区 TG</mark>：<https://t.me/aptos_cn>

<mark style="background-color:green;">中文社区 Twitter</mark>：<https://twitter.com/aptos_cn>

<mark style="background-color:green;">中文社区 Medium</mark>：<https://medium.com/@aptos_cn>

<mark style="background-color:green;">中文 Wiki</mark>：[https://wiki.aptos.movemove.org](https://wiki.aptos.movemove.org/)

<mark style="background-color:green;">MoveMove 生态社区</mark>：<https://discord.gg/Vsj7Jwny9G>

### 🤿 想了解更多？[​](https://aptos.dev/#-want-to-dive-in) <a href="#want-to-dive-in" id="want-to-dive-in"></a>

从基础开始，例如帐户（[Accounts](/ji-chu/zhang-hu-account)）的工作方式。 您可以直接进入我们本篇教程。&#x20;

或者你也可以直接阅读[源码](https://github.com/aptos-labs/aptos-core)，帮我们为大家搭建 Layer 1。

### **✨ 开发网重置**

我们将在每周五早上重置 devnet。&#x20;

您可以加入我们的开发者网络以获取通知并查看 Discord 中的最新更新。

### ⚔️ 请注意安全[​](https://aptos.dev/#%EF%B8%8F-be-safe-out-there) <a href="#be-safe-out-there" id="be-safe-out-there"></a>

请记住，我们的社区管理员绝不会先向您发送消息或 DM，他们也绝不会要求您向他们汇款或分享任何敏感、私人或个人信息。&#x20;

如果您遇到这种情况，请在我们的 Discord 中向我们报告，或发送电子邮件至 <security@aptoslabs.com>

### 💪 加入我们[​](https://aptos.dev/#-join-our-crew) <a href="#join-our-crew" id="join-our-crew"></a>

想加入一个伟大的团队，致力于解决令人惊叹的世界级问题吗？ 看看我们的正在招聘的岗位，和我们一起建造吧！

招聘岗位：<https://boards.greenhouse.io/aptoslabs>

### 👾 玩得开心！[​](https://aptos.dev/#-have-fun) <a href="#have-fun" id="have-fun"></a>

我们很高兴您能来到这里，并期待认识您。 我们都在一起🦍

欢迎来到 Aptos 社区！


# 概览


# 起源

来源：[The Genesis of Aptos](https://medium.com/aptoslabs/the-genesis-of-aptos-ff98d86e9445)

作者：[Mo Shaikh](https://www.linkedin.com/in/moalishaikh/)

日期：2022/2/24

今天是 Aptos 的起源。我们的使命是为数十亿人创造对分散资产的普遍和公平的访问。

> Aptos 是一个新的独立项目，旨在实现我们提供<mark style="color:orange;">世界上最安全和最适合生产的区块链</mark>的愿景。

尽管区块链已经走了很长一段路，但它们仍然充满了危险和混乱，这已不是什么秘密。去中心化运动难以达到逃逸速度，因为不能将可靠性、安全性和可用性视为理所当然。总的来说，这些问题阻碍了从普通个人到世界上最大的企业的采用。需要一个新的 1 层区块链，专门用于解决这些问题。 <mark style="color:orange;">Aptos 的设计重点是绝对安全、可扩展的和可信的中立性</mark>——我们亲身了解并本能地理解的价值观。&#x20;

> 我们不是从零开始。

我们是 Diem 的最初创造者、研究人员、设计师和建造者，Diem 是最初为此目的而构建的区块链。虽然世界从未看到我们建造了什么，但我们的工作还远未结束。

多年来，我们有幸与世界上一些最聪明的工程师一起思考这些问题。自从离开 Meta（前身为 Facebook）以来，我们已经能够将我们的想法付诸实践，摒弃官僚主义的繁文缛节，并从头开始建立一个全新的网络，将它们付诸实践。我们现在准备更广泛地宣传这些想法，并开始交付我们一直在完善的基础设施。

Aptos 将部分建立在我们在过去三年中开源的技术之上。 Aptos 使用的是最初为 Diem 开发的安全可靠的语言 Move。我们当时构想的想法仍然适用，并将作为安全、可扩展、可升级的 Web3 的重要基础。我们的去中心化和无许可访问计划进展迅速，并将以开源的形式开发。

> 开放是实现可信中立的唯一途径。

我们拥有多年大规模开发和部署系统的经验，我们的整个团队都将我们的职业生涯奉献给了这一使命。我的联合创始人 [Avery Ching](https://www.linkedin.com/in/averyching/)是构建分布式系统并为数十亿用户扩展系统的世界领先专家之一。 Aptos 创始团队的其他成员由一群令人印象深刻的博士、研究人员、工程师、设计师和战略家组成，其中包括 [Alden Hu](https://www.linkedin.com/in/yuxuan-hu-254b1115/), [Alin Tomescu](https://www.linkedin.com/in/alinush/), [Dahlia Malkhi](https://www.linkedin.com/in/dahlia-malkhi-8b836a5/) , [David Wolinsky](https://www.linkedin.com/in/davidiw/), [Greg Nazario](https://www.linkedin.com/in/gnazario/), J[ake Skinner](https://www.linkedin.com/in/jakedskinner/), [Josh Lind](https://www.linkedin.com/in/joshdavidlind/), [Max Kaplan](http://linkedin.com/in/maxkaplan/), [Mo Ahmed](https://www.linkedin.com/in/motalent/), [Neil Harounian](https://www.linkedin.com/in/neil-harounian/), [Rati Gelashivili](https://www.linkedin.com/in/rati-gelashvili/), [Rustie Lin](https://www.linkedin.com/in/rustielin/), [Sasha Spiegelman](https://www.linkedin.com/in/alexander-spiegelman-00aaa9152/), [Sherry Xiao](https://www.linkedin.com/in/sherryxiao/), [Sital Kedia](https://www.linkedin.com/in/sital-kedia/) 和 [Zekun Li](https://www.linkedin.com/in/zekun-li-51a82560/)

作为一个团队，我们坚信自己是构建最安全、最具可扩展性和最广泛可访问网络的合适人选。这可能听起来陈词滥调或过于田园诗般，但我们全心全意地表示：世界公用事业无法在少数人手中取得成功。 Aptos 只有在所有人共同使用、拥有和维护的情况下才能成功。

现在，在共识协议的第四次迭代中，我们相信 Aptos 能够为数十亿互联网用户的日常生活带来可负担性、去中心化和速度。它是可用的最低延迟、乐观响应的 BFT 协议，它具有强大的链上信誉系统和用于并行执行的新颖方法 - 大规模速度的关键推动力。我们将在性能、功能和整体用户体验改进方面快速部署许多关键创新，并利用我们在此过程中无缝进行重大升级的能力。

在接下来的几周内，Aptos 的 devnet 将启动，为世界各地的开发人员提供一个可以探索的游乐场。此后不久，将有一个激励性测试网，然后是主网，我们预计将在今年晚些时候推出。我们还将开始公布一些已经承诺与 Aptos 长期合作的合作伙伴。

> 来和我们一起建造吧。

我邀请大家提供意见并加入 Aptos 社区。我们正在寻找建设者、验证者、用户、品牌、创造者和信徒。如果是您，请在 [Twitter](https://twitter.com/aptoslabs) 上关注我们并查看我们的 [job board](https://boards.greenhouse.io/aptoslabs)。

Aptos（Ohlone 意为“人民”）包含了我们的使命和精神，即我们为什么要建立。

-Mo、Avery 和 Aptos 团队


# 愿景

来源：[The Aptos Vision](https://medium.com/aptoslabs/the-aptos-vision-1028ac56676e)

作者：[Avery Ching](https://www.linkedin.com/in/averyching/)

正如 [Aptos 的起源](/gai-lan/qi-yuan) 中所述，我们的使命是为数十亿人创造对去中心化资产的普遍和公平的访问。 web3 堆栈庞大而丰富，从智能合约平台到开发人员基础设施和工具、钱包和去中心化应用程序。在未来几年，我们希望与生态系统参与者建立并合作，以改善整个空间。 Aptos 之旅始于与去中心化社区合作<mark style="color:orange;">部署一个安全、可扩展和可升级的智能合约平台</mark>。

今天有许多智能合约平台部署在生产环境中——每个平台都有自己独特的优势和权衡。我们相信，需要一个快速创新和适应性强的区块链来满足当今和未来数十亿人的需求。我们的信念是，用户体验需要在安全性和可扩展性方面得到显着改善，才能接触到大众（尤其是非加密原生用户）。在过去三年中，我们的团队已经能够研究和开发创新理念（请参阅下面的链接以及我们的众多出版物）并将它们安全地部署到生产环境中。下面，我们将简要概述我们的计划以及我们将如何支持对网络进行频繁和快速的升级。在接下来的几个月里，我们将分享一系列关于实现我们目标的当前和新举措的深入探讨和随附的技术论文。

## 安全和可靠 <a href="#f7e2" id="f7e2"></a>

数十亿互联网用户已经熟悉购买商品和服务以及交换或存储价值。为了加快互联网用户对 web3 的采用，我们需要在区块链层面取得一些进步，以进一步增强 web3 堆栈的其余部分。以下是我们团队当前关注的一些领域，这些领域推动了安全性和可靠性。

### **Move** 语言：安全灵活的区块链语言

[Move 语言](https://github.com/aptos-labs/aptos-core/blob/main/developer-docs-site/static/papers/aptos-move-a-language-with-programmable-resources/2020-05-26.pdf) 专为在区块链上进行安全资源管理和可验证执行而设计。事务执行是确定性的、封闭的和计量的。确定性和封闭性意味着交易执行的输出是完全可预测的，并且仅基于交易中包含的信息和当前分类帐状态。计量是针对事务执行级别的拒绝服务攻击的重要防御措施。 [Move 证明者](https://arxiv.org/pdf/2110.08362.pdf) 可以通过表达性规范语言正式验证 Move 模块的属性，并且速度足够快，可以作为持续集成测试的一部分运行。Move 资源，受线性类型启发，静态确保资源得到保存，不会被复制或意外破坏——完全避免了一整类潜在的攻击。我们的区块链和 Move 语言是在三年多前同时共同开发的。账户、交易费用、标准库、验证节点管理和配置都通过 Move 实现。我们喜欢 Move 的安全性，并且很高兴能帮助围绕它建立一个广泛的生态系统。

### 生产级、高保证 BFT 共识协议

我们的团队开发了生产级、低延迟拜占庭容错 (BFT) 引擎。在过去三年中，我们已经实施了该[协议的第四次迭代](https://github.com/diem/diem/blob/main/developers.diem.com/static/papers/diem-consensus-state-machine-replication-in-the-diem-blockchain/2021-08-17.pdf)（最先进的 [HotStuff](https://arxiv.org/abs/1803.05069) 衍生产品）。在此期间，我们在私有主网环境中升级了共识协议，该环境具有多样化的运营商和零停机时间。我们对 [BFT 协议的第一个实现](https://github.com/aptos-labs/aptos-core/blob/main/developer-docs-site/static/papers/aptos-consensus-state-machine-replication-in-the-aptos-blockchain/2019-06-28.pdf) 添加了一个主动起搏器，它使用超时来同步验证器，这比等待增加的超时要快得多。随着我们对协议的最新改进，区块只需两次网络往返即可提交，从而使亚秒级的最终确定性成为常见情况。我们[新颖的信誉系统](https://arxiv.org/abs/2110.00960)分析链上状态并自动更新领导者轮换以适应无响应的验证者，而无需任何人为干预，使其非常适合去中心化环境。此外，我们的协议清楚地将活性与安全区分开来。无论网络不可达或非安全核心以某种方式受到损害，只要 BFT 诚实保证得到维护，链就不会分叉。我们的共识协议的安全性已经过审计和[正式验证](https://www.garillot.net/talks/2020-05-27-mechanized-proof-libra)。

[我们的研究](https://arxiv.org/pdf/2101.03715.pdf)表明，可以通过利用超过 2f+1 个节点对一个块进行投票的时间段来增加我们当前 BFT 协议的阻力的简单扩展。这些更强有力的承诺提供了更高的信心，即网络不会在未来分叉，并进一步提高用户对超越标准 BFT 属性的最终确定性的信心。

### **Aptos Core 的实现和测试**

在不同环境中运行的大量验证器对于去中心化和安全性都很重要。此外，验证器软件必须安全地设计以防止攻击——这是我们选择 Rust 和 Move 作为实现协议和智能合约逻辑的首选语言的主要原因之一。

Aptos 区块链的核心安全属性依赖于验证器、Move 模块和 Move VM 的正确实现。我们已经将代码模块化并确定了一个最小[可信计算库](https://en.wikipedia.org/wiki/Trusted_computing_base)（TCB），它隔离了关键的安全属性（例如共识协议的安全性和执行正确性）。模块化和 TCB 的使用使我们能够利用高保证开发技术。此外，对于安全关键组件，我们可以对代码审查、依赖关系、测试和执行环境安全实施更严格的要求。

我们将数十年的互联网规模开发、测试和部署软件的集体经验与在去中心化环境中可靠运行的新想法结合起来。在每次代码提交之前，我们都会运行一系列分布式网络测试，例如负载测试和拜占庭攻击，然后评估正确性和性能结果。在主网部署之前，我们还分阶段向多个测试网络推出，每一步都进行了大量的负载和寿命测试。我们即将在第二季度推出的激励性测试网将重点关注构建关于节点操作的社区学习。我们已经在与世界领先的节点运营商合作准备和测试各种灾难恢复场景方面拥有丰富的经验，并期待构建最可靠的去中心化网络。

### 更安全的帐户密钥恢复和轮换协议

为了防止密钥被盗，Aptos 支持任何帐户轮换其私钥的能力。验证者还可以定期轮换他们的共识密钥，以提高安全性。为了避免数十亿美元的价值因密钥丢失而被锁定的问题，我们还致力于开发可直接集成到区块链账户模型中的密钥恢复新技术。

### 多代理交易

[多代理交易](https://github.com/diem/dip/blob/main/dips/dip-169.md) 利用 Move 的签名者类型，允许在单个交易中跨多个链上账户进行任意数量的原子操作。这可以在单个交易中完成两个或多个链上实体之间的原子交换、K-of-N 批准以及任何任意原子操作。使这些复杂的交互不可分割可以提高安全性和性能。当在单个账户中结合多代理交易和多签名支持时，可以实现更广泛的可组合模式和用例。

## 可扩展性和性能 <a href="#id-6062" id="id-6062"></a>

众所周知，高交易费用、低吞吐量和高最终率限制了区块链用例，并推动了对性能优化的 L1 和 L2 区块链的需求。可扩展性的一个不太受欢迎的方面是链上存储非常昂贵，并且在扩展到数十亿个账户和数万亿个链上和链下资产时面临性能挑战。我们的目标是全面关注用户体验的性能。我们认为 L1 区块链应该承担更多的可扩展性责任，以简化和主流化用户体验。

### 端到端指标和测量

多年来，围绕区块链性能指标存在巨大的混乱。让我们从最简单的定义开始。

* Throughput（吞吐量） — 每秒处理的事务数 (tps)
* Finality（最终性） — 从客户端创建并提交交易到另一方确认交易已提交的时间

吞吐量和最终性受许多因素影响。首先是交易的复杂性。将代币从 Bob 的账户转移到 Alice 的账户的简单点对点交易比预先生成 1,000 个 NFT 的交易便宜得多。验证者和账户的大小和分布也会对性能产生很大影响。有多少个验证器，它们的硬件规格是什么？有多少个账户（例如 10,000 对 10 亿个账户）？帐户大小分布是多少（例如 1k 与 10 MB）？什么是访问模式分布（例如零事务冲突到完全顺序依赖）？交易之间的排序保证（部分排序或全排序）是什么？它们是否非常适合智能合约？这些因素使得很难客观地比较不同网络之间的性能。在考虑衡量绩效的不同方法时，比较会更加令人困惑。例如，我们对一系列基于 DAG 的共识协议（即  [All You Need Is DAG](https://dl.acm.org/doi/abs/10.1145/3465084.3467905), [Narwhal and Tusk: A DAG-based Mempool and Efficient BFT Consensus](https://arxiv.org/abs/2105.11827) 和 [Bullshark: DAG BFT Protocols Made Practical](https://arxiv.org/pdf/2201.05677.pdf)）的研究表明，这类协议可以范围从 125,000 到 160,000 tps 甚至更高。然而，这些吞吐量数字并不代表端到端的区块链吞吐量，因为它们只考虑了共识、网络和部分存储方面的考虑。它们没有考虑其他重要因素，例如生产区块链中的交易执行时间、帐户访问模式或经过身份验证的数据结构（例如 Merkle 树）。在最终性方面，区块时间经常被误认为是最终性，而实际上，区块时间是最终性的一个输入因素。

我们相信基准测试的透明度和开放性，解释测试方法之间的差异，并清楚地说明组件与端到端结果。在接下来的几个月中，我们将分享详细的端到端性能发现和测试方法。随着时间的推移，我们打算分享基准测试框架并比较不同区块链上各种用例的性能特征。

### 通往高吞吐量和快速终结的旅程

这一旅程的一个重要步骤是将共识协议与交易执行完全分离。共识协议就提议的交易顺序达成一致。在单独的协议中和关键路径之外，验证者执行交易并就最终交易排序和执行结果达成一致。集成共识和执行的协议更简单，但由于它们的相互依赖关系，在吞吐量和延迟方面付出了更高的成本。

如前所述，我们拥有经过多年测试的最低延迟、最先进的 HotStuff 变体。在最常见的情况下，该协议承诺在两个完整的网络往返中达成一致——即使全球网络往返需要多达 250 毫秒，也很容易达到亚秒级。当受到攻击或网络中断期间，链上信誉系统会自动将关闭验证者的负面影响降至最低。我们已经开始研究和开发下一个共识协议迭代，以推进交易传播，并期待在今年晚些时候使用这项技术升级测试网。

在 [将共识协议与执行管道解耦](https://github.com/diem/dip/pull/214) 后，下一个瓶颈是交易执行时间。使用[受软件事务内存启发的新技术](https://arxiv.org/abs/2203.06871)，我们在仅执行基准测试中仅使用 32 个内核实现了每秒超过 130k 的事务。开发人员可以免费获得这种好处，因为我们的执行框架会自动利用任何工作负载中固有的并行性。无需提供来自开发人员的额外信息。也许最重要的是，高吞吐量的区块链意味着用户的低交易费用。

性能的最后一个瓶颈——也是最被忽视的——是经过身份验证的数据结构和相关的状态存储。一个挑战是，在验证账本状态（例如账户余额、智能合约等）时，内存中的 Merkle 树在小规模上是有效的，但是，将大型 Merkle 树写入持久存储是一个瓶颈。我们正在通过探索更高的分支因子、访问模式优化的缓存和仔细的版本控制来设计我们经过身份验证的数据结构，使其对数据库友好。我们目前还在开发对大型帐户的支持——能够访问每个帐户的单个 Move 资源，而不是作为单个 blob 低效地访问，并通过将 Move 资源拆分为访问模式优化的块来研究未来在 Move 资源中进行更精细访问的路径。分层存储和状态租金是支持所有类型的新用例的重要优先事项。

### 并行账户交易，同时保留对交易排序的控制

以太坊普及的序列号方法为用户提供了对交易排序、高效内存池过滤和有限内存使用的完全控制。我们已经尝试使用[抗冲突的序列号](https://github.com/diem/dip/blob/main/dips/dip-168.md)来增强序列号方法，以允许帐户在序列号窗口上并行，同时仍然允许用户在必要时控制交易排序。我们未来的工作是考虑更灵活和可组合的并行性的替代实现。

### 对轻量级、完整、归档和验证节点的快速灵活的状态管理支持

对于高吞吐量区块链，节点之间的状态同步可能是 CPU 密集型的。我们支持一系列不同的状态同步协议，这些协议可以权衡 CPU 和网络带宽。为了支持廉价的全节点，我们有一个协议，可以同步交易及其执行结果，由法定人数的验证者签名，允许节点以更高的网络吞吐量为代价跳过计算，并直接从已执行的账本状态更新账本状态结果。

客户端无需像大多数区块链那样下载区块链来获取最新的账本，而是可以使用顶级 [交易累加器](https://github.com/aptos-labs/aptos-core/blob/main/developer-docs-site/docs/reference/glossary.md#merkle-accumulator) 来获取最新提交的交易。如果需要，这还允许对以前的交易和分类帐历史进行廉价的修剪。[受信任的航路点](https://github.com/diem/diem/issues/1384) 还允许节点快速同步以快速赶上。

## 可升级性 <a href="#ba77" id="ba77"></a>

网络必须快速适应支持不断变化的 web3 需求。区块链用例和技术改进不断增长和跨越式发展。例如，在 2021 年，NFT 成为一个 [400 亿美元的市场](https://www.ft.com/content/e95f5ac2-0476-41f4-abd4-8a99faa7737d)。许多人预测 2022 年将是 DAO 获得采用的一年。正在开发新的共识协议和智能合约语言。隐私保护交易变得越来越实用。不幸的是，今天的许多网络在启动后都难以做出重大的协议改进。一些尝试进行重大升级的网络经历了数小时的停机时间，有时还经历了意外的硬分叉。我们认为这是推出如此多新网络的关键原因之一。不幸的是，网络的爆炸式增长带来了围绕用户体验的挑战，因为它需要熟悉不同的环境并找出最适合特定用例的网络。我们设计和构建的 Aptos 区块链可作为关键资产进行升级，并在过去几年中成功执行了几次重大升级而没有停机。为了实现这一目标，验证者的管理和配置通过链上状态进行管理——方便社区投票和快速执行升级。强大的测试和部署实践确保安全可靠的部署。

## Aptos 区块链历史 <a href="#id-121c" id="id-121c"></a>

Aptos 技术栈由 30 多人的加密平台研究和工程团队研究、设计和开发，他们具有共识、密码学、分布式系统、形式验证、安全等领域的专业知识和博士学位。代码库已大幅增长超过 3+年拥有 292 位独特的开发人员和数十个社区驱动的改进建议。它通过许多不同的用例在生产环境中进行了实战测试，并由众多企业运营商运营了一年多，有十几种可能的灾难场景。我们在零停机时间的私有主网上进行了多次成功的重大升级。该代码库是开源和社区管理的，其长期目标是构建可用于许多网络的基础设施。社区已经准备好并且很高兴看到这项技术的发布并解锁许多新的应用程序。


# 主网路线图

我们致力于尽快启动我们的主网，以使更广泛的社区能够开始建设——许多人已经等待我们的技术发布多年。在通往主网及以后的旅程中，我们将宣布一系列全球黑客马拉松，期待您的参与！一旦在我们的测试网和部署过程中达到企业就绪状态，我们将利用我们的能力通过新特性、功能、安全性和扩展性改进来升级网络。

2022 Q1（3 月 15 日）——开发者测试网发布

* 与战略合作伙伴和 web3 开发者社区合作，收集反馈并改进 Move 开发者体验和 Move 语言

2022 Q2 — 激励性测试网启动

* 供战略合作伙伴和 web3 开发人员构建的更大且类似于主网的测试平台
* 与节点运营商社区合作，共同运营去中心化网络并建立专业知识
* 启动漏洞赏金以改善开发人员体验、节点操作并解决基础架构问题
* 为所有有助于保护网络的参与者提供激励机制

2022 Q3 — 主网发布

2022 Q4–2023 Q1 — 将下一个主要版本部署到 Aptos 主网，并具有下一组重要功能


# 激励测试网（1）

通往 Aptos 主网的道路要经过一系列激励性测试网。在此过程中，我们期待 Aptos 社区齐心协力，共同构建一个安全、可靠、去中心化的网络。下面我们描述了我们的时间表，概述了可交付成果和目标。确切的目标和奖励将随着激励测试网的注册开放而公布。虽然本文件旨在详尽说明剩余的工作，但由于在此过程中发现了新的机遇和挑战，我们打算在时间表上保持灵活性。

我们激励性测试网的主要目标很简单：确保该社区已准备好启动生产级 Aptos 主网。节点运营商、开发人员、审计员和生态系统建设者都受邀参与，并将因其对这一目标的贡献而获得奖励。我们的社区将参与一些评估和演习，以验证核心开发人员测试的结果，并为我们所有人的主网运营做好准备。在这个激动人心的时刻，我们邀请并欢迎整个 Aptos 社区的所有反馈，并将举办频繁的社区会议来倾听、迭代和改进这些计划。

## 概述 <a href="#id-4a42" id="id-4a42"></a>

### IT1（激励测试网 1）：去中心化启动

* 5月13日注册
* 5月24日开始
* 主要成果
  * 注册和跟踪网站将在 IT1 开始前提供
  * 最初限制为 100 个验证节点（将在后续阶段扩展）
  * 使用 GitHub 的去中心化网络启动工具
  * 运行生产级验证器的文档
* 目标
  * 使用 GitHub / Genesis 工具执行去中心化网络启动
  * 通过 Aptos 衡量的测试网完成保持在线并参与

### IT2：质押

* 预计开始时间：六月
* 主要成果
  * 测试币支持质押和质押奖励
  * 去中心化水龙头支持
  * 更新 gas schedule，例如，改进交易中每个操作的成本并有据可查，以使交易成本可预测
* 目标
  * 验证质押、奖励和委托是否按预期工作
  * 激励验证者通过水龙头提供代币，以支持测试币的分发、验证者的委托和生态系统参与

### IT3：治理和升级

* 预计开始时间：7月
* 主要成果
  * 链上投票与治理
  * 通过备份和恢复技术实现轻量级状态同步
* 目标
  * 通过链上治理进行去中心化网络升级
  * 支持新的验证者通过备份和恢复加入网络

### IT4：动态验证器拓扑

* 预计开始时间：8月
* 主要成果
  * 支持尽可能多的验证者
  * 验证者 DDoS 防护
* 目标
  * 迁移到主网前的最后热身
  * Validator DDoS验证

## 社区机会

为了帮助建立改变游戏规则的主网启动势头，我们还计划邀请教育、开发和增长领域的技术和非技术贡献者。 我们勇敢的 Aptonauts 社区将协调探索 Aptoverse 的任务。 这样，每个人都有机会参与进来，并从高质量的参与中获得回报。 我们正在公开讨论一些激励项目：

1. 适应性（教育）
2. 赏金+黑客马拉松（开发）
3. 创意挑战（成长）&#x20;

我们将很快分享有关如何参与此 Aptos 社区计划的更多信息。 跳转到我们的 [Discord 频道](https://discord.gg/aptoslabs)进行讨论，并留意新的 Aptos 社区论坛。

## FAQ <a href="#cc7f" id="cc7f"></a>

### 什么样的行为会得到奖励？

对于每个可交付成果和目标，Aptos Labs 将在 Aptos 社区的支持下确定适当的成功标准和激励措施，以了解我们在该领域的进展。一些评估可能必须重复进行，可能需要改进成功标准和不同的激励措施。在开始与社区进行每次评估之前，我们将明确确定成功标准和激励措施。

### 注册过程会是什么样子？

我们仍在研究加入激励测试网的要求以及注册过程将包括的内容。最低限度，用户需要能够提供经过验证的 GitHub 帐户才能参与该过程，因为这是 Genesis 仪式工具的依赖项。

### 什么时候会有更多更新？

随着激励性测试网的发布越来越近，我们将分享更多细节。我们在下面概述的路线图中指出了其中许多模棱两可的地方。

### 什么时候发布代币经济学和白皮书？

我们正在积极评估代币经济学的不同方法，并预计在我们运行激励测试网时会发布某种形式。同样，我们的白皮书将涵盖我们认为主网将准备好的工作，这些工作也即将发布。

### 您能否详细说明验证者的参与规模？

在 IT1 期间，我们将从 100 名参与者开始。我们谨慎地设置了这个数字，因为这是软件堆栈第一次在野外运行。在内部，我们评估了更大的网络。随着社区对部署和管理 Aptos 节点的理解，我们预计在随后的每一轮中都会增加参与者的数量。通过 IT4，我们预计允许尽可能多的验证者。


# 技术路线图

从现在到激励性测试网结束，我们将通过许多技术贡献升级 Aptos 区块链。其中许多在下面列出，其中大部分可以通过我们的  [GitHub repository](https://github.com/aptos-labs/aptos-core) 中的问题进行跟踪：

### 区块链核心堆栈

联网

* 全节点的链上发现——不再需要做广告
* 改进的网络拓扑，例如区分上游、下游和对等点
* 验证者的 DDoS 缓解和评估——来自下游全节点和验证者的攻击
* 内联运行状况检查以减少网络堆栈上不必要的争用

共识

* 动态验证者集、小组委员会
* 删除遗留的共识代码
* 通过参考模型进行深度安全规则集成，以最大限度地减少复制开销
* 通过环境变量或配置参数查询密钥
* 共识数据库 (rocksdb) 中的安全数据存储

存储

* 细粒度存储——当前模型利用 Blob 存储来限制单个帐户的最大存储容量。包括本机表扩展。
* CRSN（抗冲突序列号）——使用符合条件的序列号窗口而不是顺序字段
* 存储经济学，反状态爆炸。gas 和状态驱逐。
* 性能基准测试和改进，包括每个块而不是每个事务状态身份验证结构维护。

状态同步

* 使用最新状态和状态输出增量而不是事务进行同步
* 用于端点的备份和恢复适配器，例如 AWS S3 存储桶
* 共识和状态同步之间的智能、性能意识和共识中的角色（验证者集或追随者）

内存池

* 改进的交易序列化和粘性，包括对同一账户交易的并行支持
* 消除或限制验证者广播交易

执行器

* 启用事务的并行执行
* 减少从交易到区块的 merkle 树更新粒度
* 去除执行正确性可信计算库

虚拟机 (VM) / 框架

* 对模块的可变性进行更严格的强制执行，例如，暂时冻结版本并保留开放的治理能力以启用它
* 来自非核心框架模块的错误类型或更丰富的错误
* 映射到实际使用情况的现代化 gas schedule ——我们仍在使用传统的 gas schedule，在未来的可配置性方面几乎没有提供任何帮助。&#x20;
* 支持可并行化的序列号，允许单个帐户的“乱序”事务执行，也称为抗冲突序列号

操作工具

* 验证者和全节点创建和注册
* 密钥和地址轮换
* 与状态同步协作备份和恢复
* 深度健康检查器
* 检索和打印受管节点信息
* 检索和打印节点的链上信息
* 验证全节点/验证器部署，例如，运行独立的测试网

智能合约和框架

* 权益证明、委托和奖励
* 治理/DAO 升级框架替换根账户
* DeFi 或交换应用程序（需要社区支持！）

性能调优

* 使用 perf 评估浪费并在验证器和全节点之间应用有价值的更改
* 统一 Tokio 执行器以减少争用

### 生态系统

* 索引器提供更丰富的数据视图，例如不同交易类型的计数和查找
* 激励性测试网注册和监控网站
* 钱包原型/参考实现 Web 扩展和原生
  * 硬币转移
  * NFT / Token发布、查看、转移
  * WalletConnect (2.0) 支持
* 可视化
  * 从生态系统中收集关于节点和工具使用情况的指标
  * 验证器和全节点仪表板/记分板
* 支持 Aptos 本地测试网/开发网的 VS 代码插件
* Aptos-core、Aptos 的 Move 和 Aptos 的框架的文档
* Aptos + Move CLI 以及文档
* 使用各种工具支持节点部署。提供部署文档（terraform、helm、kubernetes、docker）

**请在** [**GitHub**](https://github.com/aptos-labs/aptos-core) **上为我们加注星标，并在** [**Discord**](https://discord.gg/DP4GN9Atjv) **上加入我们的行列！**


# 激励测试网（2）

自从我们第一次分享我们的激励测试网计划以来，已经过去了两周。我们很高兴分享一些关于资格要求、选择过程、预期参与名额、成功标准和奖励的激动人心的更新。

在进入细节之前，我们想分享一些 Aptos 激励测试网 (AIT) 流程的原则和目标：

* 建立和促进将导致 Aptos 主网成功的运营商行为
* 强调网络的分散性
* 帮助为潜在的验证者和全节点运营商建立一个生态系统
* 选择过程、成功标准以及网络成功和失败的公平和透明

对于以上几点，我们认识到我们在 AIT1 注册和网络启动之间的时间线将参与者置于非理想位置。当我们探索注册过程、资格要求和启动时间时，这一点就暴露出来了。为了让所有人都能获得更公平的体验，我们调整了 AIT1 的时间表：

* 5 月 13 日 - 注册日期保持不变
* 5 月 19 日 - 报名的最后一天
* 5 月 23 日 - 录取通知
* 5 月 24 日 - AIT1 开始

## 资格要求和注册 <a href="#id-4f3e" id="id-4f3e"></a>

为了参与激励测试网，参与者必须展示配置和部署节点的能力，并提供某些识别信息（姓名、住所或住所、出生日期等）并通过制裁筛选。 5 月 13 日，我们将启动一个门户网站，其中包含一个注册页面、有关如何配置和部署节点的说明以及节点和注册状态页面。我们的注册站点将验证您是否已成功准备您的节点。此时，您将能够提交某些识别信息以进行验证和筛选。成功完成这些任务后，您将被指定为“AIT 合格”。筛选过程通常很快，但在某些情况下可能需要时间才能完成。

由于美国境内的监管考虑，美国参与者可以参与，但没有资格获得基于代币的奖励。我们正在积极探索为美国的成功参与者提供其他非代币形式奖励的可能性，但不能保证美国参与者可以获得任何此类奖励。

只有在主网成功启动后，才会提供与激励测试网相关的任何奖励，并且提供的任何代币都将有一年的锁定期。

## 选择过程 <a href="#c504" id="c504"></a>

要被选中，您必须首先通过所有资格要求。除此之外，我们还将考虑其他几个方面，包括地理分布和申请人声誉。作为注册过程的一部分，申请人将被要求指定他们打算部署其节点的区域，以确保验证者的地理分布。在前几轮比赛中失败或表现出不诚实行为的参与者将没有资格参加下一轮比赛。还将考虑申请人作为节点运营商的声誉。最终，我们希望实现普遍参与，以帮助巩固该网络的启动。

## 参与者、成功和奖励 <a href="#id-29ee" id="id-29ee"></a>

### **AIT1:** 去中心化启动 <a href="#c06f" id="c06f"></a>

* 参与者数量：100
* 成功标准\
  – 成功启动节点并在 24 小时内通过 liveness 检查\
  – 可用性超过 95%
* 奖励（仅限非美国参与者）：每位成功参与者 500 Aptos 代币

### **AIT2:** 质押 <a href="#id-85a9" id="id-85a9"></a>

* 参与者数量：100-500 个节点
* 成功标准\
  – 成功启动节点并在 24 小时内通过 liveness 检查\
  – 可用性超过 97%\
  – 前 75% 的共识参与者（共识投票）
* 奖励（仅限非美国参与者）\
  – 前 3 名投票者节点分别获得 5000、2500 和 1000 个 Aptos 代币\
  – 每位成功参与者 500 Aptos 代币

### **AIT3:** 治理和升级 <a href="#be72" id="be72"></a>

* 参与者数量：1000
* 成功标准\
  – 在节点启动时间的 12 小时内成功启动节点并通过活跃度检查\
  – 可用性超过 97%\
  – 成功参与标准网络升级，2天响应时间\
  – 成功参与修补程序网络升级，12 小时响应时间\
  – 前 75% 的共识参与者（共识投票）
* 奖励（仅限非美国参与者）\
  – 前 3 名投票者节点分别获得 5000、2500 和 1000 个 Aptos 代币\
  – 每位成功参与者 500 Aptos 代币

### **AIT4:** 动态验证器拓扑 <a href="#id-3979" id="id-3979"></a>

* 参与者数量：1000+
* 成功标准\
  – 在节点启动时间的 12 小时内成功启动节点并通过活跃度检查\
  – 其余标准仍在确定中
* 奖励（仅限非美国参与者）： 每位成功参与者 500 Aptos 代币，额外奖励正在考虑中

### 活性检查被定义为验证者节点的以下能力： <a href="#id-5f44" id="id-5f44"></a>

* 将指标推送到 Aptos 遥测
* 指标端口上报指标
* 连接到 Aptos 的官方验证节点
* 参与共识

### 可用性定义为您的验证节点： <a href="#id-59ad" id="id-59ad"></a>

* 每小时至少提出一次提案
* 连接到 Aptos 的官方验证器节点

我们将在 5 月 13 日注册网站启动时发布公告。同时，在 GitHub 上为我们加注星标，在 Twitter 上关注我们，并在 Discord 上加入我们的行列！

## 下一步是什么 <a href="#b19b" id="b19b"></a>

我们将在 5 月 13 日注册网站启动时发布公告。同时，在 GitHub 上为我们加注星标，在 Twitter 上关注我们，并在 Discord 上加入我们的行列！


# 激励测试网（3）

AIT2 总结 & 第三期里程碑公布

发布时间：8月1日

我们最近完成了一个非常成功的 AIT2，它使 Aptos 社区更加紧密地联系在一起。感谢您的参与，让 AIT2 比 AIT1 更成功！自 AIT2 结束以来，我们已经花时间评估结果，并希望分享有关我们的激励测试网和今年秋季推出主网的详细信息的更新。

在深入了解 AIT2 回顾之前，这里有一些关键更新：

* Aptos Labs 仍然专注于启动 Aptos 主网，为社区带来最安全、最快和最可靠的 Web3 构建基础。
* 以前的 AIT 告诉我们，激励性测试网从准备和注册到执行，再到最后的分析，都需要时间。为了继续促进健康的启动路径并保持我们对 AIT 的期望，AIT3 将是我们在主网启动之前的最后一个激励测试网。
* AIT4 和其他 AIT 将在主网之后发生。具体来说，我们将继续为软件的每次重大升级托管激励测试网。
* 我们的路线图由社区反馈和经验驱动。因此，AIT3 和 AIT4 的一些功能已推迟到后主网。
* 我们的 AIT3 选择池大小将与 AIT2 相似，因为我们专注于改进主网启动之前的网络。
* 该团队计划在下个月冻结主网的代码，此后不久，我们将发布一个长期存在的测试网，以更好地进行社区协作并为主网做准备。

关键日期：

* AIT3 注册 — 8 月 19 日
* 代码冻结 — 8 月 25 日
* AIT3 发布——8 月 30 日
* AIT3 结束 — 9 月 9 日
* 主网发布——秋季
* AIT4 — 冬季

## AIT2 亮点和奖励 <a href="#dbd2" id="dbd2"></a>

在社区的热情和奉献精神的推动下，AIT2 是又一次成功的测试。社区节点超过 225 个，其中 26 个由 Aptos 和 Aptos 合作伙伴运营。这些节点分布在全球 44 个国家和 110 个城市。

![](https://miro.medium.com/max/1400/0*IuiC6pFmKZyKuacK)

首先是奖励！团队认可了这么多人所做的努力，并修改了奖励范围以使其更具包容性。

要求是：

* 按时完成所有要求的目标，包括在 24 小时内加入验证者集，并在网络结束时退出验证者集。
* 满足指标推送数据 ≥ 95% 定义的节点活跃度。绝大多数参与者都达到了这个目标！
* 每小时一项共识提案所定义的参与率 ≥ 95%。尽管考虑到我们增加了负载和数据刷新延迟，但我们将其降低到了 90%。
* 在至少 25% 的共识轮次中投票。

奖励如下：

* 全额奖励：500 Aptos 代币，用于满足以上所有条件的人
* 部分奖励：300 Aptos 代币，适用于那些能够在活跃度和参与度以及其他标准方面达到至少 50% 的人
* 奖励：符合以下每个标准的 200 个 Aptos 代币：\
  – 前 10% 并至少升级一次。\
  – 将节点保留在偏远地区的运营商，同时满足上述所有要求。因为它们帮助我们更好地了解去中心化节点分布如何影响性能的细微差别。\
  – 在帮助我们识别关键问题和做出重大贡献方面做得非常出色的运营商。

注意：出于监管方面的考虑，Aptos 代币只能提供给非美国参与者。只有在主网成功启动后，才会提供与激励测试网相关的任何奖励，并且提供的任何代币都将至少有一年的锁定期。[我们的条款和条件](https://www.aptoslabs.com/terms-testnet/)中提供了更多详细信息。

## 我们的成功 <a href="#id-3e25" id="id-3e25"></a>

1. 社区听到了！社区中的英雄们得到了认可，并帮助我们改进了我们的流程，并指导其他运营商完成了这一流程。社区的协作性质真正体现了 Aptos 的精神。
2. 激励性测试网仍然是验证软件的好方法。该团队与操作员一起能够识别故障以及赢得绩效的机会。Aptos Labs 团队和运营商成功协调了四次软件升级，网络上的停机时间为零。其中最值得注意的是一次升级，将带宽消耗减少了 50% 以上。
3. 运营商很专心，帮助我们更好地了解部署复杂的去中心化软件的细微差别。在这个阶段，我们探索了一种基于投票的奖励算法。一些参与者迁移到确保他们更有可能投票的地区。那些仍然没有帮助我们验证被放置在偏远地区可能具有挑战性并且需要更好地了解延迟和带宽限制的方法。

## 我们的教训 <a href="#id-1df4" id="id-1df4"></a>

1. 基于投票抑制地域分配的奖励。在我们的共识协议中，提议者只等待前 ⅔ 的权益加权投票，从而限制了地理位置偏远的节点有效参与的机会。当运营商试图最大化其投票性能时，这会导致偏向于区域内的节点集群。该团队正在积极致力于更好的奖励设计，旨在减少这种偏见。社区设置节点的多种方式帮助我们了解不同的配置如何影响协议。
2. 这种经验使我们能够发现机会来改善操作员在工具和部署方面的体验，从而显着降低成本和运营。一些运营商受到网络变化的影响，我们尚未将网络地址更新添加到我们的工具集中。同样，运营商帮助我们注意到带宽的高使用率，这导致我们提供软件升级以减少开销并在主网之前将更多时间投入到这个空间中。

## 接下来的是 <a href="#cfbc" id="cfbc"></a>

感谢您在我们迈向主网时的持续支持以及 AIT2 带来的成功和学习。在接下来的几周里，我们将分享关于 AIT3 的更深入的更新。[我们也欢迎对我们新推出的论坛](https://forum.aptoslabs.com/)提出问题和评论。在此之前，请继续在[Twitter 上](https://twitter.com/AptosLabs)关注我们并在[GitHub 上](https://github.com/aptos-labs/aptos-core)为我们加星。


# 激励测试网（4）

AIT-3 激励测试网第三期开启

![](https://miro.medium.com/max/700/1*ymwwqVCRO0fZxlHrzXBUZA.png)

Aptos 团队对[AIT-2](/gai-lan/ji-li-ce-shi-wang-3)的成功感到兴奋，并为即将到来的 AIT-3 准备了很多东西！该团队一直在努力准备大量更新。以下是 AIT-3 的一些注册细节和期望。

这个激励性的测试网带来了主网的最后一个主要交付物：链上治理和升级！为了反映主网启动的现实环境，AIT-3 参与者的数量将与 AIT-2 几乎相同，因此大约有 225 名参与者。我们的目标是确保 Aptos 能够安全可靠地启动主网。在即将到来的冬天的某个时候，我们将带来 AIT-4，重点是扩展去中心化和进一步的性能优化。我们期待届时邀请更多的参与者！

完成以下 6 个步骤以注册 AIT-3：

1. 确认您拥有[Discord](https://discord.com/)帐户并已加入[Aptos 服务器](https://discord.gg/aptoslabs)。\
   **重要提示**：您将被要求为 AIT-3 连接 Discord 帐户。
2. [在社区网站](https://aptoslabs.com/incentivized-testnet)上创建一个帐户（或登录您现有的帐户）。
3. 出现提示时，提供您的电子邮件地址和用户名，并确保确认您的电子邮件。
4. 按照此[AIT-3 页面](https://aptos.dev/nodes/ait/ait-3)上的步骤安装和启动验证器节点，将节点设置为测试模式，并向 Aptos 社区提供验证器详细信息。
5. 下载并[安装 Petra 钱包](/jiao-cheng/petra-qian-bao-cha-jian-an-zhuang-shi-yong)并在社区网站上注册您的钱包地址。（删除以前版本的钱包）
6. 通过身份验证和制裁筛选流程。如果您对资格有疑问，请查看我们的[条款和条件](https://www.aptoslabs.com/terms-testnet/)。

**提醒**：个人**只能注册一次**。我们保留将滥用此政策的人排除在 AIT 之外的权利，他们将没有资格获得任何奖励。

里程碑：

* 8 月 19 日开始报名
* 8 月 25 日截止报名
* 通知将于 8 月 29 日发出
* 测试网于 8 月 30 日上线
* 测试网于 9 月 9 日结束

**警告**：所有被接受的参与者都应在收到通知后 24 小时内[加入测试网](/jiao-cheng/ce-shi-wang-testnet/she-qu-jiao-cheng/jia-ru-ce-shi-wang)。

## 主要目标 <a href="#id-3d51" id="id-3d51"></a>

在 AIT-2 中，Aptos 社区齐聚一堂，证明 Aptos 可以作为去中心化的权益证明区块链运行！在此过程中，你们中的许多人继续展示成功的 Aptos 节点运营商应具备的属性。作为一个社区，我们能够尝试增加系统的负载，在全球分布的参与者中工作，甚至还进行了一些升级练习。

AIT-3 的一个关键新体验是 Aptos 治理结构中的各种角色。每个节点都由*所有者*帐户中的权益支持；但是，节点本身可以​​由独立于*所有者帐户的授权操作员*帐户管理。这意味着，与 AIT-2 不同，节点运营商和代币所有者现在可以是独立的实体。同样，在治理中，所有者可以不同于投票者。这意味着所有者可以将他们的投票权委托给一个单独的*选民*账户。在 AIT-3 中，操作者帐户将通过 CLI 进行管理，而*所有者*和*投票者*账户将通过 Petra 钱包进行管理。Explorer 还扩展了其功能，以支持所有者*和*投票者*操作*的[治理平台。](https://explorer.devnet.aptos.dev/proposals)

为了对区块链进行压力测试，AIT-3 将有 3 种不同类型的治理提案：(1) 简单的配置更改，(2) Move 框架升级，以及 (3) 需要 Move 框架升级和更新的二进制文件。实现所有这些目标将有力地表明 Aptos 软件和社区都已准备好启动此区块链！

所有选定的参与者都应：

* 访问 Aptos 治理平台以注册、质押和指定其操作者（与验证人关联的帐户）
* 在链上注册他们的验证节点的配置
* 加入验证器集
* 对 3 个不同的治理提案进行投票
* 将验证器设置在 AIT-3 的末尾

**文档**：有关这些主题的文档发布在此AIT-3 页面上。

## 成功标准和奖励 <a href="#e8c1" id="e8c1"></a>

* 按时完成所有要求的目标
* 满足指标推送数据定义的节点活跃度 ≥ 95%
* 获得的链上奖励 ≥ 最大值的 80%
* 成功参与 Aptos 创建的所有治理操作

符合这些条件的每个人都将获得 800 个 Aptos 代币。此外，团队将提供两次额外获得 200 Aptos 代币的机会：

* 使用开放的 REST API 运行验证器全节点以进行负载测试
* 成功参与所有操作

**注意**：出于监管方面的考虑，任何 Aptos 代币只能提供给非美国参与者。只有在主网成功启动后，才会提供与激励测试网相关的任何奖励，并且提供的任何代币都将至少有一年的锁定期。[我们的条款和条件](https://www.aptoslabs.com/terms-testnet/)中提供了更多详细信息。

## 下一步 <a href="#id-6429" id="id-6429"></a>

成功注册后，请查找 Aptos Labs 于 8 月 27 日发送的电子邮件。同时，在[GitHub 上给我们“star”，在](https://github.com/aptos-labs/aptos-core)[Twitter](https://twitter.com/aptoslabs)上关注我们，并将有关 AIT-3 注册的任何问题带到下周的 Move Monday。


# 基础


# 账户（Account）

账户代表 Aptos 区块链上可以发送交易的资源。 每个帐户由一个特定的 16 字节帐户地址标识，并且是 Move 模块和 Move 资源的容器。

## 介绍

每个账户的状态包括 Code 和 Data：

* **Code**: Move 模块包含 Code（类型和过程声明），但它们不包含 data。 该模块的程序对更新区块链全局状态的规则进行编码。
* **Data**: Move 资源包含数据但没有 Code。 每个资源值都有一个在区块链分布式数据库中发布的模块中声明的类型。

一个帐户可能包含任意数量的 Move 资源和 Move 模块。

### 初始帐户设置[​](https://aptos.dev/basics/basics-accounts/#initial-account-setup) <a href="#initial-account-setup" id="initial-account-setup"></a>

Aptos 帐户由一个帐户地址引用，该地址是一个 32 字节的值。 帐户地址源自该帐户的初始公共验证密钥。 具体来说，帐户地址是与签名方案标识符字节连接的初始公共验证密钥的 SHA-3 256 加密散列的最后 32 个字节。 Aptos 区块链支持两种签名方案：[Ed25519](https://aptos.dev/reference/glossary#ed25519) 和 MultiEd25519（用于多重签名交易）。 帐户的私钥是签署交易所必需的。

每个账户还存储一个 `sequence_number`，代表下一个交易的序列号，防止交易重放攻击。 在创建帐户时将其初始化为 0。

### Authentication Keys[​](https://aptos.dev/basics/basics-accounts/#authentication-keys) <a href="#authentication-keys" id="authentication-keys"></a>

每个帐户都存储一个身份验证密钥。此身份验证密钥使帐户所有者能够轮换与帐户关联的私钥，而无需更改托管其帐户的地址。在轮换期间，身份验证密钥会根据新生成的私有、公共密钥对进行更新。

#### 单签名者身份验证[​](https://aptos.dev/basics/basics-accounts/#single-signer-authentication) <a href="#single-signer-authentication" id="single-signer-authentication"></a>

要生成身份验证密钥和帐户地址：

1. **生成密钥对**：生成新的密钥对 (`pubkey_A`, `privkey_A`)。Aptos 区块链在 Ed25519 曲线上使用 PureEdDSA 方案，如 RFC 8032 中所定义。
2. **派生一个 32 字节的身份验证密钥**：派生一个 32 字节的身份验证密钥 `auth_key = sha3-256(pubkey_A | 0x00)`, 其中 | 表示连接。 `0x00` 是一个 1 字节的签名方案标识符，其中 0x00 表示单签名。 auth\_key 的前 16 个字节是“认证密钥前缀”。 auth\_key 的最后 16 个字节是账户地址。任何创建账户的交易都需要账户地址和认证密钥前缀，但与现有账户交互的交易只需要地址。

#### 多重签名者身份验证[​](https://aptos.dev/basics/basics-accounts/#multisigner-authentication) <a href="#multisigner-authentication" id="multisigner-authentication"></a>

帐户的身份验证密钥可能需要单个签名或多个签名（“multisig”）。使用 K-of-N 多重签名身份验证时，帐户总共有 N 个签名者，并且必须使用这 N 个签名中的至少 K 个来对交易进行身份验证。

创建 K-of-N 多重签名认证密钥类似于创建单个签名密钥：

1. **生成密钥对**：生成 N 个 ed25519 公钥 `p_1`, ..., `p_n`.
2. **派生一个 32 字节的身份验证密钥**：计算 `auth_key = sha3-256(p_1 | … | p_n | K | 0x01)`。如上所述派生地址和身份验证密钥前缀。 K 表示验证交易所需的 K-of-N。 0x01 是一个 1 字节的签名方案标识符，其中 0x01 表示多重签名。

### 账户资源[​](https://aptos.dev/basics/basics-accounts/#account-resources) <a href="#account-resources" id="account-resources"></a>

Aptos 上的每个帐户都可以存储数据，它在资源中存储。初始资源是帐户数据本身（身份验证密钥和序列号）。可以在创建帐户后添加其他资源，例如货币或 NFT。为了创建帐户，Aptos 测试网需要帐户的公钥和一定数量的 TestCoin 才能添加到该帐户，从而使用这两种资源创建一个新帐户。


# 事件（Events）

在事务执行期间发出事件。 每个 Move 模块都可以定义自己的事件，并选择在执行模块时何时发出事件。 例如，在一次 [代币转账](https://github.com/aptos-labs/aptos-core/blob/5d80b0d5fe09dbbf3e190459cdc376e8333ad6a3/aptos-move/framework/aptos-framework/sources/TestCoin.move#L152) 中，发送方和接收方的账户都会分别发出 `SentEvent` 和 `ReceivedEvent`。 这些数据存储在账本中，可以通过 REST 接口的 [Get events by event handle](https://fullnode.devnet.aptoslabs.com/spec.html#/operations/get_events_by_event_handle) 进行查询。

假设账户 `0xcaa60eb4a01756955ab9b2d1caca52ed` 向另一个账户发送了代币，可以对 REST 接口进行以下查询：`https://fullnode.devnet.aptoslabs.com/accounts/caa60eb4a01756955ab9b2d1caca52ed/events/0x1::TestCoin::TransferEvents/sent_events`。输出将是存储在该帐户上的所有 SentEvents，如下所示：

```json
[{
  "key":"0x0000000000000000caa60eb4a01756955ab9b2d1caca52ed",
  "sequence_number":"0",
  "type":"0x1::TestCoin::SentEvent",
  "data":{
    "amount":"1000",
    "to":"0xaabcd0d03bd1e135ccf806a652c5831f"
  }
}]
```

每个注册的事件都有一个唯一的 `key`。 这个 key `0x0000000000000000caa60eb4a01756955ab9b2d1caca52ed` 映射到在帐户 `0xcaa60eb4a01756955ab9b2d1caca52ed` 上注册的事件 `0x1::TestCoin::TransferEvents/sent_events`。 然后可以使用此密钥直接进行事件查询，例如 `https://fullnode.devnet.aptoslabs.com/events/0000000000000000caa60eb4a01756955ab9b2d1caca52ed`。

这些代表事件流，或事件列表，其中每个条目包含从 0 开始的顺序增加的序列号、类型和数据。 每个事件必须由某种类型定义。 可能存在由相同或相似类型定义的多个事件，尤其是在使用泛型时。 事件具有关联的数据。 一般原则是包含所有必要的数据，以了解在执行更改数据并发出事件的事务之前和之后对底层资源的更改。


# 证明（Proof）

Aptos 区块链使用证明来验证区块链数据的真实性和正确性。

Aptos 区块链中的所有数据都存储在一个单一版本的分布式数据库中。每个验证器和 FullNode 的[存储](/ji-chu/yan-zheng-zhe-jie-dian#storage)负责将商定的事务块及其执行结果持久保存到数据库中。区块链被表示为一个不断增长的 [Merkle 树](https://aptos.dev/reference/glossary#merkle-trees)，其中每个附加到树的叶子代表由区块链执行的单个事务。

区块链执行的所有操作和所有账户状态都可以加密验证。这些加密证明确保验证者节点就状态达成一致。通过支持证明，客户端不需要信任它从中接收数据的实体。例如，如果客户从一个帐户中获取最后 n 笔交易，则证明可以证明在响应中没有添加、省略或修改任何交易。客户端还可以查询帐户的状态，询问是否处理了特定交易，等等。


# Gas 和交易费

当在 Aptos 区块链上执行交易时，会以 gas 跟踪和衡量资源使用情况。

## 介绍

Gas 确保在 Aptos 区块链上运行的所有 Move 程序最终终止。这限制了所使用的计算资源。 Gas 还提供了收取交易费用的能力，部分基于执行期间消耗的资源。

当客户端向 Aptos 区块链提交要执行的交易时，它包含指定的：

* `max_gas_amount`: 可用于执行交易的最大气体单位数量。这限制了事务可以消耗的计算资源。
* `gas_price`: 指定 gas 货币的 gas 价格。 Gas 价格是一种将 Gas 单位（虚拟机消耗的资源的抽象单位）转换为指定 Gas 货币的交易费用的一种方式。
* `gas_currency`: 交易费用的货币。

向客户收取的交易费用最多为 `gas_price * max_gas_amount`。

随着资源供需的波动，gas 价格以及交易费用应遵循 Aptos 区块链的市场特征。

### 资源使用类型[​](https://aptos.dev/basics/basics-gas-txn-fee#types-of-resource-usage) <a href="#types-of-resource-usage" id="types-of-resource-usage"></a>

为了让虚拟机（VM）执行交易，gas 系统需要跟踪交易使用的主要资源。这些分为三个资源维度：

1. 执行交易的计算成本。
2. 通过 Aptos 生态系统传播交易的网络成本。
3. 在 Aptos 区块链上交易执行期间创建和使用的数据的存储成本。

这些资源中的前两个（计算和网络）是短暂的。但是，第三个（存储）是长久的。分配数据后，该数据将一直存在，直到被删除。在帐户的情况下，数据无限期地存在。

这些资源维度中的每一个都可以独立波动。但是，也只有一个 gas 价格。因此，这意味着必须正确跟踪每个维度的 gas 使用量，因为 gas 价格仅作为总 gas 使用量的一个乘数。因此，交易的 gas 使用量需要与执行交易相关的实际成本密切相关。

### 用 gas 计算交易费用[​](https://aptos.dev/basics/basics-gas-txn-fee#using-gas-to-compute-transaction-fees) <a href="#using-gas-to-compute-transaction-fees" id="using-gas-to-compute-transaction-fees"></a>

当您发送交易时，执行的交易费用（以指定的 gas 货币表示）是 gas 价格乘以 VM 为该交易计算的资源使用量。

在事务流的不同时间，对资源使用的不同方面进行收费。 下图详细介绍了交易流程的基础知识和与 gas 相关的逻辑：

![FIGURE 1.0 Gas and Transaction Flow](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F6EJhidXvwNzHJlHbRrEQ%2Fimage.png?alt=media\&token=b01f06eb-88ab-45f9-b2be-88493baf852c)

&#x20;在图中，prologue 和 epilogue 部分都用相同的颜色标记。这是因为交易流的这些部分不需要被计量：

* 在 prologue 中，不知道提交账户是否有足够的资金来支付其 gas 责任，或者提交交易的用户是否甚至对提交账户有权限。由于缺乏这种知识，在执行 prologue 时，需要不计量。为 prologue 失败的交易扣除 gas 可能会导致未经授权的账户扣除。
* epilogue 部分负责从提交帐户中扣除执行费用并进行分配。正因为如此，即使事务执行已经耗尽了 gas，epilogue 也必须运行。同样，我们不希望它在向提交账户扣款时耗尽 gas，因为这会导致执行额外的计算而不收取任何交易费用。

这意味着最低交易费用 MIN\_TXN\_FEE 需要足以支付运行 prologue 和 epilogue 的平均成本。

在 prologue 运行后，我们已经部分检查了该帐户是否可以承担其 gas 责任，其余的交易流程从 max\_gas\_amount 的“gas tank”满开始。对 MIN\_TXN\_FEE 进行充值，然后为 VM 执行的每条指令向 gas tank 充值。这种每条指令的扣除一直持续到：

* 交易执行完成，之后收取存储交易数据的费用，运行 epilogue 并扣除执行费用，或&#x20;
* “gas tank”变空，在这种情况下会引发 OutOfGas 错误。&#x20;

在前者中，收取费用并将交易结果保存在 Aptos 区块链上。在后者中，当出现错误时，事务的执行将停止。之后，收集交易的总 Gas 花费。除了案例中的扣除外，没有其他的执行剩余。

### 使用 gas 来确定交易的优先级[​](https://aptos.dev/basics/basics-gas-txn-fee#using-gas-to-prioritize-a-transaction) <a href="#using-gas-to-prioritize-a-transaction" id="using-gas-to-prioritize-a-transaction"></a>

当您发送交易时，它会根据不同的标准进行优先级排序。其中之一是交易的标准化 gas 价格。

对于需要按 gas 价格排序的交易（即非治理交易），这些价格首先被标准化为 Aptos Coins。这是通过使用存储在链上的当前 gas 货币到 Aptos Coin 的转换率来完成的。然后根据这个标准化的 gas 价格（部分）对交易进行排名。

例如：

* Bob 发送了一笔交易， `gas_price` 是 10， `gas_currency` 是 “BobCoins”。
* Alice 在相同时间也发送了一笔交易，`gas_price` 是 20，`gas_currency` 是 “AliceCoins”.

如果链上“BobCoins”对 Aptos Coins 的汇率是 2.1，而链上“AliceCoins”对 Aptos Coins 的汇率是 1，

* Bob 的交易标准化 gas 价格是 `10 * 2.1 = 21`。
* Alice 的交易标准化 gas 价格是 `20 * 1 = 20`。

然后，Bob 的交易排名将高于 Alice 的交易。

### 核心设计原则[​](https://aptos.dev/basics/basics-gas-txn-fee#core-design-principles) <a href="#core-design-principles" id="core-design-principles"></a>

三个核心原则驱动了 Aptos 和 Move 中 gas 的设计：

| 设计原则                 | 描述                                                                                                                              |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Move 是图灵完备的          | <p>因此，不能静态地确定给定的 Move 程序是否终止。 但是，通过确保</p><ul><li>每条字节码指令都有非零的gas消耗，并且</li><li>任何程序可以启动的 gas 量是有限的。</li></ul><p>我们拥有程序的终止属性。</p> |
| 阻止 DDoS 攻击并鼓励明智地使用网络 | 交易的 gas 使用量与该交易的资源消耗相关。 gas 价格以及交易费用应该随着网络中的竞争而上升和下降。 在启动时，我们预计 gas 价格将处于或接近于零。 但在竞争激烈的时期，您可以使用 gas 价格优先处理交易，这将帮助在此期间仅发送重要交易。   |
| 程序的资源使用需要达成共识        | 这意味着计算资源消耗的方法需要是确定性的。 这排除了跟踪资源使用的其他方法，例如循环计数器或任何类型的基于时间的方法，因为它们不能保证在节点之间是确定性的。 跟踪资源使用情况的方法需要抽象。                                 |


# 节点网络和同步

## 节点网络拓扑

验证器节点和全节点形成一个层次结构，验证器节点位于根部，全节点位于其他任何地方。 Aptos 区块链区分两种类型的全节点：验证者全节点和公共全节点。 验证器全节点直接连接到验证器节点并提供可扩展性以及 DDoS 缓解。 公共 FullNodes 连接到 Validator FullNodes（或其他公共 FullNodes）以获得对 Aptos 网络的低延迟访问。

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FkfqvpatoMCA405bdAxzz%2Fimage.png?alt=media\&token=551561bb-7017-48bb-afd1-5b20aaeb7379)

### 独立的网络堆栈[​](https://aptos.dev/basics/basics-node-networks-sync#separate-network-stacks) <a href="#separate-network-stacks" id="separate-network-stacks"></a>

Aptos 区块链支持各种网络拓扑的不同网络堆栈。 例如，验证者网络独立于 FullNode 网络。 拥有单独的网络堆栈的优点包括：

* 不同网络之间的清晰分离。
* 更好地支持安全首选项（例如，双向与服务器身份验证）。
* 允许隔离发现协议（即验证节点公共端点的链上发现与私有组织的手动配置）。

## 节点同步

Aptos 节点通过两种机制同步到 Aptos 区块链的最新状态：共识或状态同步。 验证节点将使用共识和状态同步来保持最新状态，而全节点仅使用状态同步。

例如，验证器节点将在第一次上线或重启时调用状态同步（例如，在离线一段时间后）。 一旦验证者与区块链的最新状态保持同步，它将开始参与共识并完全依赖共识来保持最新状态。 然而，随着区块链的增长，FullNodes 会持续依赖状态同步来获取并保持最新状态。

### 状态同步器[​](https://aptos.dev/basics/basics-node-networks-sync#state-synchronizer) <a href="#state-synchronizer" id="state-synchronizer"></a>

每个 Aptos 节点都包含一个[状态同步器](https://github.com/aptos-labs/aptos-core/tree/main/state-sync)组件，用于将节点的状态与其对等节点同步。 该组件对所有类型的 Aptos 节点具有相同的功能：它利用专用的点对点网络不断请求和传播区块链数据。 验证者节点在验证者节点网络内分发区块链数据，而全节点依赖于其他全节点（即验证者或公共全节点）。

### 同步 API[​](https://aptos.dev/basics/basics-node-networks-sync#synchronization-api) <a href="#synchronization-api" id="synchronization-api"></a>

Aptos 节点的状态同步器与其他节点的状态同步器通信以获取和发送事务块。 在[此处](https://github.com/aptos-labs/aptos-core/tree/main/documentation/specifications/state_sync)的规范中了解有关其工作原理的更多信息。


# 验证者节点

Aptos 节点是 Aptos 生态系统的一个实体，用于跟踪 Aptos 区块链的状态。 客户端通过 Aptos 节点与区块链交互。 有两种类型的节点：

* 验证者节点（本篇）
* [全节点](/ji-chu/quan-jie-dian)

每个 Aptos 节点都包含几个逻辑组件：

* [REST service](https://aptos.dev/reference/glossary#rest-service)
* [Mempool](#mempool)
* [Consensus (disabled in FullNodes)](#consensus)
* [Execution](#execution)
* [Virtual Machine](#virtual-machine-vm)
* [Storage](#storage)
* [State synchronizer](#state-synchronizer)

[Aptos-core](https://aptos.dev/reference/glossary#aptos-core) 软件可以配置为作为验证节点或全节点运行。

## 概述

当一笔交易提交到 Aptos Blockchain 时，验证者节点运行分布式共识协议（[consensus protocol](https://aptos.dev/reference/glossary#consensus-protocol)），执行交易，并将交易和执行结果存储在区块链上。验证者节点决定哪些交易将被添加到区块链中以及以何种顺序添加。

Aptos 区块链使用拜占庭容错 (BFT) 共识协议，让验证者节点就最终交易的账本及其执行结果达成一致。验证器节点处理这些交易并将它们包含在区块链数据库的本地副本中。这意味着最新的验证器节点始终在本地维护区块链当前状态（[state](https://aptos.dev/reference/glossary#state)）的副本。

验证器节点通过专用网络直接与其他验证器节点通信。 全节点（[Fullnodes](/ji-chu/quan-jie-dian)）是最终交易历史的外部验证和/或传播资源。它们从对等点（peers）接收交易并可能在本地重新执行它们（与验证器执行交易的方式相同）。 FullNode 将重新执行的交易结果存储到本地存储中。这样做，他们可以挑战验证者的任何犯规行为，并在有任何尝试重写或修改区块链历史时提供证据。这有助于减轻验证者的腐败和/或勾结。

> AptosBFT 共识协议为多达三分之一的恶意验证节点提供容错能力。

### 验证者节点组件[​](https://aptos.dev/basics/basics-validator-nodes/#validator-node-components) <a href="#validator-node-components" id="validator-node-components"></a>

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F9WF5XV9e69IWgx7kqqrI%2Fvalidator.png?alt=media\&token=5adf7a8c-660a-43a3-9494-3086d136c8e4)

#### Mempool[​](https://aptos.dev/basics/basics-validator-nodes/#mempool) <a href="#mempool" id="mempool"></a>

Mempool 是每个节点中的一个组件，它保存已提交到区块链但尚未商定或执行的交易的内存缓冲区。 此缓冲区在验证器节点和 FullNode 之间复制。

FullNode 的 JSON-RPC 服务将交易发送到验证节点的内存池。 Mempool 对交易进行各种检查，以确保交易的有效性并防止 DOS 攻击。 当一个新的交易通过初始验证并被添加到内存池中时，它会被分发到网络中其他验证者节点的内存池中。

当验证者节点暂时成为共识协议的领导者时，共识会从内存池中提取交易并提出新的交易块。 该区块被广播给其他验证者，并包含该区块中所有交易的总排序。 然后每个验证者执行区块并提交是否接受新区块提案的投票

#### Consensus[​](https://aptos.dev/basics/basics-validator-nodes/#consensus) <a href="#consensus" id="consensus"></a>

共识（Consensus）是负责通过与网络中的其他验证节点参与共识协议来对交易块进行排序并就执行结果达成一致的组件。

#### Execution[​](https://aptos.dev/basics/basics-validator-nodes/#execution) <a href="#execution" id="execution"></a>

执行器（Execution）是协调事务块执行并保持瞬态状态的组件。 共识投票在这个短暂的状态。 执行器维护执行结果的内存表示，直到共识将块提交到分布式数据库。 执行器使用虚拟机执行事务。 执行器充当系统输入（由交易表示）、存储（提供持久层）和虚拟机（用于执行）之间的粘合层。

#### Virtual machine (VM)[​](https://aptos.dev/basics/basics-validator-nodes/#virtual-machine-vm) <a href="#virtual-machine-vm" id="virtual-machine-vm"></a>

虚拟机 (VM) 用于在每个交易中运行 Move 程序并确定执行结果。 节点的内存池使用虚拟机对交易进行验证检查，而执行器使用虚拟机执行交易。

#### Storage[​](https://aptos.dev/basics/basics-validator-nodes/#storage) <a href="#storage" id="storage"></a>

存储（Storage）组件用于将商定的交易块及其执行结果保存到本地数据库。

#### State synchronizer[​](https://aptos.dev/basics/basics-validator-nodes/#state-synchronizer) <a href="#state-synchronizer" id="state-synchronizer"></a>

节点使用它们的状态同步器（State synchronizer）组件来“赶上”区块链的最新状态并保持最新状态。


# 全节点

Aptos 节点是 Aptos 生态系统的一个实体，用于跟踪 Aptos 区块链的状态。 客户端通过 Aptos 节点与区块链交互。 有两种类型的节点：

* [验证者节点](/ji-chu/yan-zheng-zhe-jie-dian)
* 全节点（本篇）

每个 Aptos 节点都包含几个逻辑组件：

* [REST service](https://aptos.dev/reference/glossary#rest-service)
* [Mempool](/ji-chu/yan-zheng-zhe-jie-dian#mempool)
* [Consensus (disabled in FullNodes)](#consensus)
* [Execution](#execution)
* [Virtual Machine](#virtual-machine-vm)
* [Storage](#storage)
* [State synchronizer](#state-synchronizer)

[Aptos-core](https://aptos.dev/reference/glossary#aptos-core) 软件可以配置为作为验证节点或全节点运行。

### 概览[​](https://aptos.dev/basics/basics-fullnodes#overview) <a href="#overview" id="overview"></a>

FullNodes 可以由任何人运行。 FullNode 重新执行 Aptos 区块链历史上的所有交易。 FullNodes 通过与上游参与者同步来复制区块链的整个状态，例如，其他 FullNodes 或验证器节点。 为了验证区块链状态，FullNodes 接收一组交易和验证者签名的账本的累加器哈希根（[accumulator hash root](https://aptos.dev/reference/glossary#accumulator-root-hash)）。 此外，FullNode 接受 Aptos 客户端提交的交易，并将它们直接（或间接）转发给验证者节点。 虽然 FullNode 和验证者共享相同的代码，但 FullNode 不参与共识。

第三方区块链浏览器、钱包、交易所和 DApp 可以运行本地 FullNode：

* 利用 REST 接口进行区块链交互。
* 获得 Aptos 账本的一致视图。
* 避免读取流量的速率限制。
* 对历史数据运行自定义分析。
* 获取有关特定链上事件的通知。


# 交易和状态

Aptos 区块链核心的两个基本概念是交易和状态：

* 交易：交易代表 Aptos 区块链上账户之间的数据交换（例如 Aptos 币或 NFT）。
* 状态：状态（即当前区块链账本状态）代表区块链当前状态的快照。

执行交易时，Aptos 区块链的状态会发生变化。

## 交易

当 Aptos 区块链客户端提交交易时，他们请求使用他们的交易更新账本状态。

区块链上的[签名交易](https://aptos.dev/reference/glossary#transaction)包含以下信息：

* **签名**： 发件人使用数字签名来验证他们是否签署了交易（即身份验证）。
* **发送方地址**: 发送者的 [账户地址](https://aptos.dev/reference/glossary#account-address)。
* **发送方公钥**: 对应于用于签署交易的私有认证密钥的公共认证密钥。
* **程序**: 程序包括：
  * Move 模块和函数名称或 Move 字节码交易 script。
  * script 输入的可选列表。对于点对点交易，这些输入包含接收者的信息和转账给他们的金额。
  * 要发布的 Move 字节码模块的可选列表。&#x20;
* **Gas 价格** （以指定的货币/Gas 单位表示）：这是发送方愿意为执行交易而为每单位 Gas 支付的金额。 Gas 是一种支付计算和存储费用的方式。[Gas](/ji-chu/gas-he-jiao-yi-fei#using-gas-to-compute-transaction-fees) 单位是计算的抽象度量，没有内在的现实世界价值。
* **最大 gas 数量**: [最大 gas 量](https://aptos.dev/reference/glossary#maximum-gas-amount)是允许交易消耗的最大 gas 单位。
* **Gas 货币代码**: 用于支付 Gas 的货币代码。
* **序列号**: 是一个无符号整数，在执行时必须等于发送者的帐户序列号。
* **过期时间**: 交易停止有效（即过期）之后的时间戳。

## 账本状态

Aptos 区块链的账本状态（或全局[状态](https://aptos.dev/reference/glossary#state)）包括区块链中所有账户的状态。 区块链中的每个验证者节点都必须知道区块链分布式数据库（版本化数据库）最新版本的全局状态，才能执行任何交易。

Aptos 区块链中的所有数据都保存在单一版本的分布式数据库中。 版本号是一个无符号的 64 位整数，对应于系统已执行的事务数。

### 版本化数据库[​](https://aptos.dev/basics/basics-txns-states#versioned-database) <a href="#versioned-database" id="versioned-database"></a>

Aptos 区块链中的所有数据都保存在单一版本的分布式数据库中。 版本号是一个无符号的 64 位整数，对应于系统已执行的事务数。

这个版本化的数据库允许验证节点：

* 针对最新版本的账本状态执行交易。
* 响应客户对当前和以前版本的分类帐历史的查询。

### 交易改变状态[​](https://aptos.dev/basics/basics-txns-states#transactions-change-state) <a href="#transactions-change-state" id="transactions-change-state"></a>

![FIGURE 1.0 TRANSACTIONS CHANGE STATE](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FkNe2mKUKkqStDT3YxdR9%2Fimage.png?alt=media\&token=c909360e-ee77-4b6e-a822-d09915c532ea)

图 1.0 表示执行交易 TN 如何将 Aptos 区块链的状态从 SN-1 更改为 SN。

图中：

| 名称               | 描述                                                                                                                                                           |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 账户 **A** 和 **B** | 在 Aptos 区块链上代表 Alice 和 Bob 的账户                                                                                                                               |
| **SN-1**         | 表示区块链的第 (N-1) 个状态。 在这种状态下，Alice 的账户 A 的余额为 110 Aptos Coins，Bob 的账户 B 的余额为 52 Aptos Coins。                                                                    |
| **TN**           | 这是在区块链上执行的第 N 个交易。 在本例中，它代表 Alice 向 Bob 发送 10 个 Aptos 币。                                                                                                     |
| **F**            | 它是一个确定性函数。 F 总是为特定的初始状态和特定的交易返回相同的最终状态。 如果区块链的当前状态是 SN-1，交易 TN 是在状态 SN-1 上执行的，那么区块链的新状态总是 SN。 Aptos Blockchain 使用 Move 语言来实现确定性执行功能 F。                       |
| **SN**           | 这是区块链的第 N 个状态。 当交易 TN 应用于区块链时，它会生成新的状态 SN（将 F 应用于 SN-1 和 TN 的结果）。 这导致 Alice 的账户余额减少 10 到 100 Aptos Coins，Bob 的账户余额增加 10 到 62 Aptos Coins。 新状态 SN 显示这些更新后的余额。 |


# 交易


# 与 Aptos 区块链交互

Aptos 区块链使用 [Move](https://diem.github.io/move/) 虚拟机 (VM) 来执行操作。虽然许多区块链实现了一组原生操作，但 Aptos 将所有操作委托给 Move，包括：账户创建、资金转账和发布 Move 模块。为了支持这些操作，构建在 Move 之上的区块链必须提供一个框架（类似于计算机的操作系统或最小的可行功能集）来与区块链交互。在本节中，我们将讨论这些通过 Aptos 框架的 script 函数来发布。

本指南（与 [Move 模块教程](/jiao-cheng/nin-de-di-yi-ge-move-mo-kuai)一起）将解锁在 Aptos 区块链之上开始构建丰富应用程序所需的最少信息量。注意：Aptos 框架正在大力开发中，本文档可能不是最新的。最新的框架可以在[源代码](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/framework/aptos-framework/sources)中找到。

Aptos Framework 内提供给用户的核心功能包括：

* 发送和接收 `TestCoin`
* 创建新帐户
* 发布新的 Move 模块

注意：本文档假设读者已经熟悉提交交易，如[您的第一个交易教程](/jiao-cheng/nin-de-di-yi-bi-jiao-yi)中所述。

### 发送和接收 `TestCoin`[​](https://aptos.dev/transactions/interacting-with-the-aptos-blockchain#sending-and-receiving-testcoin) <a href="#sending-and-receiving-testcoin" id="sending-and-receiving-testcoin"></a>

提交和执行交易时需要 TestCoin 来支付gas费用。 TestCoin 可以通过查询 Devnet Faucet 获得。 有关示例，请参阅您的第一次交易教程。

执行转账的 payload 是：

```json
{
  "type": "script_function_payload",
  "function": "0x1::TestCoin::transfer",
  "type_arguments": [],
  "arguments": [
    "0x737b36c96926043794ed3a0b3eaaceaf",
    "1000",
  ]
}
```

这指示 VM 执行脚本 `script` `0x1::TestCoin::transfer`。第一个参数是发送方地址， `0x737b36c96926043794ed3a0b3eaaceaf`, 第二个是要转账的金额，`1000`。发送方地址是调用此 script 发送交易的账户地址。

### 创建新账户[​](https://aptos.dev/transactions/interacting-with-the-aptos-blockchain#creating-a-new-account) <a href="#creating-a-new-account" id="creating-a-new-account"></a>

创建新帐户的 payload 是：

```json
{
  "type": "script_function_payload",
  "function": "0x1::AptosAccount::create_account",
  "type_arguments": [],
  "arguments": [
    "0x0c7e09cd9185a27104fa218a0b26ea88",
    "0xaacf87ae9d8a5e523c7f1107c668cb28dec005933c4a3bf0465ffd8a9800a2d900",
  ]
}
```

这指示 Move 虚拟机执行 `script` `0x1::AptosAccount::create_account`。第一个参数是要创建的帐户的地址，第二个参数是身份验证密钥原像（在 Accounts 中提到）。对于单一签名者身份验证，这是与 0 字节（或 `pubkey_A | 0x00`）连接的公钥。 这是防止帐户地址抢占所必需的。 该指令的执行验证验证密钥的最后 16 字节是否与 16 字节的帐户地址相同。 我们正在积极努力改进此 API，以支持接收 32 字节的帐户地址，从而消除对抢占或帐户操纵的担忧。

### 发布新的 Move 模块[​](https://aptos.dev/transactions/interacting-with-the-aptos-blockchain#publishing-a-new-move-module) <a href="#publishing-a-new-move-module" id="publishing-a-new-move-module"></a>

发布新模块的 payload：

```
"type": "module_bundle_payload",
"modules": [
    {"bytecode": "0x..."},
],
```

这指示 VM 在发送者的帐户下发布模块字节码。 如需完整教程，请参阅您的第一个移动模块。

需要注意的是，Move 字节码必须指定与发送者账户相同的地址，否则交易将被拒绝。例如，假设账户地址为 `0xe110`，Move 模块需要更新如 `module 0xe110::Message`, `module 0xbar::Message` 将被拒绝。 或者，可以使用别名地址，例如模块 `module HelloBlockchain::Message` ，但 `HelloBlockchain` 别名需要在 Move.toml 文件中更新为 0xe110。 我们正在与 Move 团队合作，并计划将编译器合并到我们的 REST 接口中以缓解此问题。


# 教程


# Petra 钱包插件安装使用

如何安装和使用 Aptos 官方钱包

要在 Chrome 浏览器上安装 Petra (Aptos Wallet) 扩展程序，请执行以下步骤：

## 一、钱包安装/更新

<mark style="color:red;">请勿随意从第三方地址下载钱包插件！！！</mark>

### 方式一：Chrome 插件市场（推荐）

* 如果版本较低，可以选择方式二；

<https://chrome.google.com/webstore/detail/petra-aptos-wallet/ejjladinnckdgjemekebdpeokbikhfci>

### 方式二：GitHub repo

#### 下载安装包

来源一：官方

GitHub 下载地址：<https://github.com/aptos-labs/aptos-core/releases?q=wallet&expanded=true>

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F5jvNbFhaQqXWO8tLI8Xn%2Fimage.png?alt=media\&token=e302e934-67a5-4eeb-85d8-e2239a9ef2a4)

#### 插件安装

浏览器打开：chrome://extensions/

右边选择“开发者模式”

![打开开发者模式](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FWvxZEvzLGbtlUl5NsSJ3%2Fimage.png?alt=media\&token=d977c81f-3fde-458c-95c4-9f2c8a586a5a)

然后选择对应的 build 文件夹：

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FwVHtFor0gh4goxNcktY8%2Fimage.png?alt=media&amp;token=35bcdc99-a960-4c45-b4ec-d473cbf50bef" alt=""><figcaption></figcaption></figure>

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FDbGbicBQZmBqh9mZp4r4%2Fimage.png?alt=media\&token=21b02d76-c5e5-4268-a416-92940db4b8bc)

现在您将在扩展程序中看到 Wallet。

## 二、创建第一个钱包账户

#### 2.1 打开钱包插件

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FvjWYE6z5fzmlHrBLELpj%2Fimage.png?alt=media\&token=d884e3c6-4aca-440d-a2b2-673e5e97a64d)

#### 2.2 设置密码

密码仅仅是用来解锁该浏览器钱包的，无法恢复钱包地址。

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F0Ivm35Q2YIeuDRR56r4o%2Fimage.png?alt=media\&token=5e6bd5b0-fbc9-415b-ac18-244196b808d1)

#### 2.3 保存助记词

<mark style="color:red;">助记词是恢复钱包账户的唯一方式，请妥善保存！！！</mark>

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F2rE2u5r0ri3ZoNEW0aTS%2Fimage.png?alt=media\&token=4b9c979d-16e6-41a4-b85d-93297604f2df)

#### 2.4 进入钱包

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FWoTYpq0q8BD7gdObJ84W%2Fimage.png?alt=media\&token=a3dfed5e-87bb-4ff6-a47c-4ecab9a852ea)

## 三、查看/备份助记词或私钥

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FEoiiOu13vMwglFBI4uC1%2Fimage.png?alt=media\&token=ee15c35a-fb34-45a0-83bb-6d8a1e1f80d5)

## 四、使用助记词或私钥恢复钱包

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMny69HV2IXRBcZA4RZGu%2Fimage.png?alt=media\&token=7b77399e-1c0a-453e-a02e-e1ed6a2d2f3a)![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FkBZZeyhBZqmjReBpoA2l%2Fimage.png?alt=media\&token=2427f31b-e09d-4285-b1fc-25320ecd038d)

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FS2ek8YqllaJnKcVNbBHI%2Fimage.png?alt=media\&token=0bc6966f-6f5c-4095-91f1-54ad3df4441c)

## 五、钱包网络切换

目前支持的网络：

* Devnet：开发网（每周五会重置）
* Testnet：测试网
* AIT：激励测试网，仅用于入选的节点操作者使用。

{% hint style="warning" %}
如果您看不到如图备选的网络，请备份私钥并更新钱包！
{% endhint %}

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FCNRkMRtB95Us7QNTlxeC%2Fimage.png?alt=media\&token=9b603462-7fb2-4107-add0-159a6f0a54c1)![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FotYGzzEvlfkXS3qUwxf7%2Fimage.png?alt=media\&token=e4175202-dd78-42f0-9edc-8fa6081b49e7)

## 六、水龙头领取

先根据第四步切换到对应网络，然后在 Home 页领取测试币。

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FFJXWxEF7qU2PkZgDyNAH%2Fimage.png?alt=media&amp;token=8b89bb1b-5bec-4ca8-ac51-9b01ef1a554c" alt=""><figcaption></figcaption></figure>


# 教程指引

这是在 Aptos 生态系统中开启开发者之旅的指南！

### Aptos 开发准备[​](https://aptos.dev/tutorials/getting-started#prepare-for-aptos-development) <a href="#prepare-for-aptos-development" id="prepare-for-aptos-development"></a>

Aptos-core 可以在 [GitHub](https://github.com/aptos-labs/aptos-core) 上获得：

```shell
git clone https://github.com/aptos-labs/aptos-core.git
cd aptos-core
./scripts/dev_setup.sh
source ~/.cargo/env
```

### 浏览教程[​](https://aptos.dev/tutorials/getting-started#explore-the-tutorials) <a href="#explore-the-tutorials" id="explore-the-tutorials"></a>

* [您的第一笔交易](/jiao-cheng/nin-de-di-yi-bi-jiao-yi)
* [您的第一个 Move 模块](/jiao-cheng/nin-de-di-yi-ge-move-mo-kuai)
* [运行本地测试网](/jiao-cheng/yun-xing-ben-di-wang-luo)
* [运行全节点](/jiao-cheng/kai-fa-wang-devnet/yun-xing-quan-jie-dian-guan-fang-jiao-cheng)

### 开发 Move 模块[​](https://aptos.dev/tutorials/getting-started#start-developing-move-modules) <a href="#start-developing-move-modules" id="start-developing-move-modules"></a>

* 阅读 Move Book
  * 中文版（由 [Starcoin](https://starcoin.org/) 团队翻译）：<https://move-book.com/cn/index.html>
  * 英文版：<https://move-book.com/>
* 了解更多关于 [interacting with the Aptos Blockchain](https://aptos.dev/transactions/interacting-with-the-aptos-blockchain)
* 开始在我们的公共 Devnet 或您自己的测试网上构建和发布您自己的模块

### Devnet 细节[​](https://aptos.dev/tutorials/getting-started#devnet-details) <a href="#devnet-details" id="devnet-details"></a>

* Faucet endpoint: [https://faucet.devnet.aptoslabs.com](https://faucet.devnet.aptoslabs.com/)
* REST interface endpoint: [https://fullnode.devnet.aptoslabs.com](https://fullnode.devnet.aptoslabs.com/)
* [Genesis](https://devnet.aptoslabs.com/genesis.blob)
* [Waypoint](https://devnet.aptoslabs.com/waypoint.txt)
* [ChainID](https://devnet.aptoslabs.com/chainid.txt)


# 您的第一笔交易

本教程详细介绍了如何生成、提交和验证提交到 Aptos 区块链的交易。 步骤如下：

* 创建一个帐户的表达形式
* 围绕 REST 接口准备一个包装类
* 在 Faucet 接口周围准备一个包装类
* 将它们组合成一个应用程序，执行并验证

{% hint style="info" %}
请从下面的代码仓库中 clone 最新代码！
{% endhint %}

以下是示例代码，可以从 github 下载：

{% tabs %}
{% tab title="Python" %}
对于本教程，将重点关注 **first\_transaction.py**。

可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python)找到代码
{% endtab %}

{% tab title="Rust" %}
对于本教程，将重点关注 **first\_transaction.py**。

可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/rust)找到代码
{% endtab %}

{% tab title="Typescript" %}
对于本教程，将重点关注 **first\_transaction.py**。

可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript)找到代码
{% endtab %}
{% endtabs %}

### Step 1) 创建一个账户的表达形式[​](https://aptos.dev/tutorials/your-first-transaction#step-1-create-a-representation-of-an-account) <a href="#step-1-create-a-representation-of-an-account" id="step-1-create-a-representation-of-an-account"></a>

每个 Aptos 帐户都有一个唯一的帐户地址。 该帐户的所有者拥有映射到 Aptos 帐户地址的公钥、私钥对，以及存储在该帐户中的身份验证密钥。 在帐户基础中可以查看更多信息。 以下片段演示了该部分中描述的内容。

{% tabs %}
{% tab title="Python" %}

```python
class Account:
    """Represents an account as well as the private, public key-pair for the Aptos blockchain."""

    def __init__(self, seed: bytes = None) -> None:
        if seed is None:
            self.signing_key = SigningKey.generate()
        else:
            self.signing_key = SigningKey(seed)

    def address(self) -> str:
        """Returns the address associated with the given account"""

        return self.auth_key()

    def auth_key(self) -> str:
        """Returns the auth_key for the associated account"""

        hasher = hashlib.sha3_256()
        hasher.update(self.signing_key.verify_key.encode() + b'\x00')
        return hasher.hexdigest()

    def pub_key(self) -> str:
        """Returns the public key for the associated account"""

        return self.signing_key.verify_key.encode().hex()
```

{% endtab %}

{% tab title="Rust" %}

```rust
pub struct Account {
    signing_key: SecretKey,
}

impl Account {
    /// Represents an account as well as the private, public key-pair for the Aptos blockchain.
    pub fn new(priv_key_bytes: Option<Vec<u8>>) -> Self {
        let signing_key = match priv_key_bytes {
            Some(key) => SecretKey::from_bytes(&key).unwrap(),
            None => SecretKey::generate(&mut rand::rngs::StdRng::from_seed(OsRng.gen())),
        };

        Account { signing_key }
    }
    /// Returns the address associated with the given account
    pub fn address(&self) -> String {
        self.auth_key()
    }

    /// Returns the auth_key for the associated account
    pub fn auth_key(&self) -> String {
        let mut sha3 = Sha3::v256();
        sha3.update(PublicKey::from(&self.signing_key).as_bytes());
        sha3.update(&vec![0u8]);

        let mut output = [0u8; 32];
        sha3.finalize(&mut output);
        hex::encode(output)
    }

    /// Returns the public key for the associated account
    pub fn pub_key(&self) -> String {
        hex::encode(PublicKey::from(&self.signing_key).as_bytes())
    }
}
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
/** A subset of the fields of a TransactionRequest, for this tutorial */
export type TxnRequest = Record<string, any> & { sequence_number: string };

/** Represents an account as well as the private, public key-pair for the Aptos blockchain */
export class Account {
  signingKey: Nacl.SignKeyPair;

  constructor(seed?: Uint8Array | undefined) {
    if (seed) {
      this.signingKey = Nacl.sign.keyPair.fromSeed(seed);
    } else {
      this.signingKey = Nacl.sign.keyPair();
    }
  }

  /** Returns the address associated with the given account */
  address(): string {
    return this.authKey();
  }

  /** Returns the authKey for the associated account */
  authKey(): string {
    let hash = SHA3.sha3_256.create();
    hash.update(Buffer.from(this.signingKey.publicKey));
    hash.update("\x00");
    return hash.hex();
  }

  /** Returns the public key for the associated account */
  pubKey(): string {
    return Buffer.from(this.signingKey.publicKey).toString("hex");
  }
}
```

{% endtab %}
{% endtabs %}

### Step 2) REST 接口 <a href="#step-2-rest-interface" id="step-2-rest-interface"></a>

Aptos 公开了一个用于与区块链交互的 [REST 接口](https://fullnode.devnet.aptoslabs.com/spec.html)。 虽然可以直接读取来自 REST 接口的数据，但以下代码片段演示了一种更优雅的方法。下一组代码片段演示了如何使用 REST 接口从 FullNode 检索分类帐数据，包括帐户和帐户资源数据。它还演示了如何使用 REST 接口来构建由 JSON 格式表示的签名交易。[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface)

{% tabs %}
{% tab title="Python" %}

```python
class RestClient:
    """A wrapper around the Aptos-core Rest API"""

    def __init__(self, url: str) -> None:
        self.url = url

```

{% endtab %}

{% tab title="Rust" %}

```rust
#[derive(Clone)]
pub struct RestClient {
    url: String,
}

impl RestClient {
    /// A wrapper around the Aptos-core Rest API
    pub fn new(url: String) -> Self {
        Self { url }
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
/** A wrapper around the Aptos-core Rest API */
export class RestClient {
  url: string;

  constructor(url: string) {
    this.url = url;
  }

```

{% endtab %}
{% endtabs %}

#### Step 2.1) 读取账户[​](https://aptos.dev/tutorials/your-first-transaction#step-21-reading-an-account) <a href="#step-21-reading-an-account" id="step-21-reading-an-account"></a>

以下是用于查询帐户数据的包装类。[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface)

{% tabs %}
{% tab title="Python" %}

```python
    def account(self, account_address: str) -> Dict[str, str]:
        """Returns the sequence number and authentication key for an account"""

        response = requests.get(f"{self.url}/accounts/{account_address}")
        assert response.status_code == 200, f"{response.text} - {account_address}"
        return response.json()

    def account_resources(self, account_address: str) -> Dict[str, Any]:
        """Returns all resources associated with the account"""

        response = requests.get(f"{self.url}/accounts/{account_address}/resources")
        assert response.status_code == 200, response.text
        return response.json()

```

{% endtab %}

{% tab title="Rust" %}

```rust
    /// Returns the sequence number and authentication key for an account
    pub fn account(&self, account_address: &str) -> serde_json::Value {
        let res =
            reqwest::blocking::get(format!("{}/accounts/{}", self.url, account_address)).unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                account_address,
            );
        }

        res.json().unwrap()
    }

    /// Returns all resources associated with the account
    pub fn account_resources(&self, account_address: &str) -> serde_json::Value {
        let res = reqwest::blocking::get(format!(
            "{}/accounts/{}/resources",
            self.url, account_address
        ))
        .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                account_address,
            );
        }

        res.json().unwrap()
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
  /** Returns the sequence number and authentication key for an account */
  async account(accountAddress: string): Promise<Record<string, string> & { sequence_number: string }> {
    const response = await fetch(`${this.url}/accounts/${accountAddress}`, {method: "GET"});
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return await response.json();
  }

  /** Returns all resources associated with the account */
  async accountResources(accountAddress: string): Promise<Record<string, any> & { type: string }> {
    const response = await fetch(`${this.url}/accounts/${accountAddress}/resources`, {method: "GET"});
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return await response.json();
  }

```

{% endtab %}
{% endtabs %}

#### Step 2.2) 提交一笔交易[​](https://aptos.dev/tutorials/your-first-transaction#step-22-submitting-a-transaction) <a href="#step-22-submitting-a-transaction" id="step-22-submitting-a-transaction"></a>

下面演示了构建、签名和等待交易的核心功能。[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface)

{% tabs %}
{% tab title="Python" %}

```python
    def generate_transaction(self, sender: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Generates a transaction request that can be submitted to produce a raw transaction that
        can be signed, which upon being signed can be submitted to the blockchain. """

        account_res = self.account(sender)
        seq_num = int(account_res["sequence_number"])
        txn_request = {
            "sender": f"0x{sender}",
            "sequence_number": str(seq_num),
            "max_gas_amount": "1000",
            "gas_unit_price": "1",
            "gas_currency_code": "XUS",
            "expiration_timestamp_secs": str(int(time.time()) + 600),
            "payload": payload,
        }
        return txn_request

    def sign_transaction(self, account_from: Account, txn_request: Dict[str, Any]) -> Dict[str, Any]:
        """Converts a transaction request produced by `generate_transaction` into a properly signed
        transaction, which can then be submitted to the blockchain."""

        res = requests.post(f"{self.url}/transactions/signing_message", json=txn_request)
        assert res.status_code == 200, res.text
        to_sign = bytes.fromhex(res.json()["message"][2:])
        signature = account_from.signing_key.sign(to_sign).signature
        txn_request["signature"] = {
            "type": "ed25519_signature",
            "public_key": f"0x{account_from.pub_key()}",
            "signature": f"0x{signature.hex()}",
        }
        return txn_request

    def submit_transaction(self, txn: Dict[str, Any]) -> Dict[str, Any]:
        """Submits a signed transaction to the blockchain."""

        headers = {'Content-Type': 'application/json'}
        response = requests.post(f"{self.url}/transactions", headers=headers, json=txn)
        assert response.status_code == 202, f"{response.text} - {txn}"
        return response.json()

    def transaction_pending(self, txn_hash: str) -> bool:
        response = requests.get(f"{self.url}/transactions/{txn_hash}")
        if response.status_code == 404:
            return True
        assert response.status_code == 200, f"{response.text} - {txn_hash}"
        return response.json()["type"] == "pending_transaction"

    def wait_for_transaction(self, txn_hash: str) -> None:
        """Waits up to 10 seconds for a transaction to move past pending state."""

        count = 0
        while self.transaction_pending(txn_hash):
            assert count < 10, f"transaction {txn_hash} timed out"
            time.sleep(1)
            count += 1
        /// Generates a transaction request that can be submitted to produce a raw transaction that can be signed, which upon being signed can be submitted to the blockchain.
    pub fn generate_transaction(
        &self,
        sender: &str,
        payload: serde_json::Value,
    ) -> serde_json::Value {
        let account_res = self.account(sender);

        let seq_num = account_res
            .get("sequence_number")
            .unwrap()
            .as_str()
            .unwrap()
            .parse::<u64>()
            .unwrap();

        // Unix timestamp, in seconds + 10 minutes
        let expiration_time_secs = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards")
            .as_secs()
            + 600;

        serde_json::json!({
            "sender": format!("0x{}", sender),
            "sequence_number": seq_num.to_string(),
            "max_gas_amount": "1000",
            "gas_unit_price": "1",
            "gas_currency_code": "XUS",
            "expiration_timestamp_secs": expiration_time_secs.to_string(),
            "payload": payload,
        })
    }

    /// Converts a transaction request produced by `generate_transaction` into a properly signed transaction, which can then be submitted to the blockchain.
    pub fn sign_transaction(
        &self,
        account_from: &mut Account,
        mut txn_request: serde_json::Value,
    ) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions/signing_message", self.url))
            .body(txn_request.to_string())
            .send()
            .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        let body: serde_json::Value = res.json().unwrap();
        let to_sign_hex = Box::new(body.get("message").unwrap().as_str()).unwrap();
        let to_sign = hex::decode(&to_sign_hex[2..]).unwrap();
        let signature: String = ExpandedSecretKey::from(&account_from.signing_key)
            .sign(&to_sign, &PublicKey::from(&account_from.signing_key))
            .encode_hex();

        let signature_payload = serde_json::json!({
            "type": "ed25519_signature",
            "public_key": format!("0x{}", account_from.pub_key()),
            "signature": format!("0x{}", signature),
        });
        txn_request
            .as_object_mut()
            .unwrap()
            .insert("signature".to_string(), signature_payload);
        txn_request
    }

    /// Submits a signed transaction to the blockchain.
    pub fn submit_transaction(&self, txn_request: &serde_json::Value) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions", self.url))
            .body(txn_request.to_string())
            .header("Content-Type", "application/json")
            .send()
            .unwrap();

        if res.status() != 202 {
            assert_eq!(
                res.status(),
                202,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        res.json().unwrap()
    }

    pub fn transaction_pending(&self, transaction_hash: &str) -> bool {
        let res = reqwest::blocking::get(format!("{}/transactions/{}", self.url, transaction_hash))
            .unwrap();

        if res.status() == 404 {
            return true;
        }

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                transaction_hash,
            );
        }

        res.json::<serde_json::Value>()
            .unwrap()
            .get("type")
            .unwrap()
            .as_str()
            .unwrap()
            == "pending_transaction"
    }

    /// Waits up to 10 seconds for a transaction to move past pending state.
    pub fn wait_for_transaction(&self, txn_hash: &str) {
        let mut count = 0;
        while self.transaction_pending(txn_hash) {
            assert!(count < 10, "transaction {} timed out", txn_hash);
            thread::sleep(Duration::from_secs(1));
            count += 1;
        }
    }
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
    /// Generates a transaction request that can be submitted to produce a raw transaction that can be signed, which upon being signed can be submitted to the blockchain.
    pub fn generate_transaction(
        &self,
        sender: &str,
        payload: serde_json::Value,
    ) -> serde_json::Value {
        let account_res = self.account(sender);

        let seq_num = account_res
            .get("sequence_number")
            .unwrap()
            .as_str()
            .unwrap()
            .parse::<u64>()
            .unwrap();

        // Unix timestamp, in seconds + 10 minutes
        let expiration_time_secs = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards")
            .as_secs()
            + 600;

        serde_json::json!({
            "sender": format!("0x{}", sender),
            "sequence_number": seq_num.to_string(),
            "max_gas_amount": "1000",
            "gas_unit_price": "1",
            "gas_currency_code": "XUS",
            "expiration_timestamp_secs": expiration_time_secs.to_string(),
            "payload": payload,
        })
    }

    /// Converts a transaction request produced by `generate_transaction` into a properly signed transaction, which can then be submitted to the blockchain.
    pub fn sign_transaction(
        &self,
        account_from: &mut Account,
        mut txn_request: serde_json::Value,
    ) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions/signing_message", self.url))
            .body(txn_request.to_string())
            .send()
            .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        let body: serde_json::Value = res.json().unwrap();
        let to_sign_hex = Box::new(body.get("message").unwrap().as_str()).unwrap();
        let to_sign = hex::decode(&to_sign_hex[2..]).unwrap();
        let signature: String = ExpandedSecretKey::from(&account_from.signing_key)
            .sign(&to_sign, &PublicKey::from(&account_from.signing_key))
            .encode_hex();

        let signature_payload = serde_json::json!({
            "type": "ed25519_signature",
            "public_key": format!("0x{}", account_from.pub_key()),
            "signature": format!("0x{}", signature),
        });
        txn_request
            .as_object_mut()
            .unwrap()
            .insert("signature".to_string(), signature_payload);
        txn_request
    }

    /// Submits a signed transaction to the blockchain.
    pub fn submit_transaction(&self, txn_request: &serde_json::Value) -> serde_json::Value {
        let res = reqwest::blocking::Client::new()
            .post(format!("{}/transactions", self.url))
            .body(txn_request.to_string())
            .header("Content-Type", "application/json")
            .send()
            .unwrap();

        if res.status() != 202 {
            assert_eq!(
                res.status(),
                202,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                txn_request.as_str().unwrap_or(""),
            );
        }
        res.json().unwrap()
    }

    pub fn transaction_pending(&self, transaction_hash: &str) -> bool {
        let res = reqwest::blocking::get(format!("{}/transactions/{}", self.url, transaction_hash))
            .unwrap();

        if res.status() == 404 {
            return true;
        }

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{} - {}",
                res.text().unwrap_or("".to_string()),
                transaction_hash,
            );
        }

        res.json::<serde_json::Value>()
            .unwrap()
            .get("type")
            .unwrap()
            .as_str()
            .unwrap()
            == "pending_transaction"
    }

    /// Waits up to 10 seconds for a transaction to move past pending state.
    pub fn wait_for_transaction(&self, txn_hash: &str) {
        let mut count = 0;
        while self.transaction_pending(txn_hash) {
            assert!(count < 10, "transaction {} timed out", txn_hash);
            thread::sleep(Duration::from_secs(1));
            count += 1;
        }
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
  /** Generates a transaction request that can be submitted to produce a raw transaction that
   can be signed, which upon being signed can be submitted to the blockchain. */
  async generateTransaction(sender: string, payload: Record<string, any>): Promise<TxnRequest> {
    const account = await this.account(sender);
    const seqNum = parseInt(account["sequence_number"]);
    return {
      "sender": `0x${sender}`,
      "sequence_number": seqNum.toString(),
      "max_gas_amount": "1000",
      "gas_unit_price": "1",
      "gas_currency_code": "XUS",
      // Unix timestamp, in seconds + 10 minutes
      "expiration_timestamp_secs": (Math.floor(Date.now() / 1000) + 600).toString(),
      "payload": payload,
    };
  }

  /** Converts a transaction request produced by `generate_transaction` into a properly signed
   transaction, which can then be submitted to the blockchain. */
  async signTransaction(accountFrom: Account, txnRequest: TxnRequest): Promise<TxnRequest> {
    const response = await fetch(`${this.url}/transactions/signing_message`, {
      method: "POST",
      headers: {"Content-Type": "application/json"},
      body: JSON.stringify(txnRequest)
    });
    if (response.status != 200) {
      assert(response.status == 200, (await response.text()) + " - " + JSON.stringify(txnRequest));
    }
    const result: Record<string, any> & { message: string } = await response.json();
    const toSign = Buffer.from(result["message"].substring(2), "hex");
    const signature = Nacl.sign(toSign, accountFrom.signingKey.secretKey);
    const signatureHex = Buffer.from(signature).toString("hex").slice(0, 128);
    txnRequest["signature"] = {
      "type": "ed25519_signature",
      "public_key": `0x${accountFrom.pubKey()}`,
      "signature": `0x${signatureHex}`,
    };
    return txnRequest;
  }

  /** Submits a signed transaction to the blockchain. */
  async submitTransaction(accountFrom: Account, txnRequest: TxnRequest): Promise<Record<string, any>> {
    const response = await fetch(`${this.url}/transactions`, {
      method: "POST",
      headers: {"Content-Type": "application/json"},
      body: JSON.stringify(txnRequest)
    });
    if (response.status != 202) {
      assert(response.status == 202, (await response.text()) + " - " + JSON.stringify(txnRequest));
    }
    return await response.json();
  }

  async transactionPending(txnHash: string): Promise<boolean> {
    const response = await fetch(`${this.url}/transactions/${txnHash}`, {method: "GET"});
    if (response.status == 404) {
      return true;
    }
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    return (await response.json())["type"] == "pending_transaction";
  }

  /** Waits up to 10 seconds for a transaction to move past pending state */
  async waitForTransaction(txnHash: string) {
    let count = 0;
    while (await this.transactionPending(txnHash)) {
      assert(count < 10);
      await new Promise(resolve => setTimeout(resolve, 1000));
      count += 1;
      if (count >= 10) {
        throw new Error(`Waiting for transaction ${txnHash} timed out!`);
      }
    }
  }

  
```

{% endtab %}
{% endtabs %}

#### Step 2.3) 编写特定的逻辑[​](https://aptos.dev/tutorials/your-first-transaction#step-23-application-specific-logic) <a href="#step-23-application-specific-logic" id="step-23-application-specific-logic"></a>

下面演示如何从区块链读取数据以及如何写入数据，例如提交特定交易。[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface)

{% tabs %}
{% tab title="Python" %}

```python
    def account_balance(self, account_address: str) -> Optional[int]:
        """Returns the test coin balance associated with the account"""

        resources = self.account_resources(account_address)
        for resource in resources:
            if resource["type"] == "0x1::TestCoin::Balance":
                return int(resource["data"]["coin"]["value"])
        return None

    def transfer(self, account_from: Account, recipient: str, amount: int) -> str:
        """Transfer a given coin amount from a given Account to the recipient's account address.
        Returns the sequence number of the transaction used to transfer."""

        payload = {
            "type": "script_function_payload",
            "function": "0x1::TestCoin::transfer",
            "type_arguments": [],
            "arguments": [
                f"0x{recipient}",
                str(amount),
            ]
        }
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        res = self.submit_transaction(signed_txn)
        return str(res["hash"])
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
    /// Returns the test coin balance associated with the account
    pub fn account_balance(&self, account_address: &str) -> Option<u64> {
        self.account_resources(account_address)
            .as_array()
            .unwrap()
            .iter()
            .find(|x| {
                x.get("type")
                    .map(|v| v.as_str().unwrap() == "0x1::TestCoin::Balance".to_string())
                    .unwrap_or(false)
            })
            .and_then(|coin| {
                coin.get("data")
                    .unwrap()
                    .get("coin")
                    .unwrap()
                    .get("value")
                    .unwrap()
                    .as_str()
                    .and_then(|s| s.parse::<u64>().ok())
            })
    }

    /// Transfer a given coin amount from a given Account to the recipient's account address.
    /// Returns the sequence number of the transaction used to transfer
    pub fn transfer(&self, account_from: &mut Account, recipient: &str, amount: u64) -> String {
        let payload = serde_json::json!({
            "type": "script_function_payload",
            "function": "0x1::TestCoin::transfer",
            "type_arguments": [],
            "arguments": [format!("0x{}", recipient), amount.to_string()]
        });
        let txn_request = self.generate_transaction(&account_from.address(), payload);
        let signed_txn = self.sign_transaction(account_from, txn_request);
        let res = self.submit_transaction(&signed_txn);

        res.get("hash").unwrap().as_str().unwrap().to_string()
    }
}
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
  /** Returns the test coin balance associated with the account */
  async accountBalance(accountAddress: string): Promise<number | null> {
    const resources = await this.accountResources(accountAddress);
    for (const key in resources) {
      const resource = resources[key];
      if (resource["type"] == "0x1::TestCoin::Balance") {
        return parseInt(resource["data"]["coin"]["value"]);
      }
    }
    return null;
  }

  /** Transfer a given coin amount from a given Account to the recipient's account address.
   Returns the sequence number of the transaction used to transfer. */
  async transfer(accountFrom: Account, recipient: string, amount: number): Promise<string> {
    const payload: { function: string; arguments: string[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::TestCoin::transfer",
      type_arguments: [],
      arguments: [
        `0x${recipient}`,
        amount.toString(),
      ]
    };
    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(accountFrom, signedTxn);
    return res["hash"].toString();
  }

}
```

{% endtab %}
{% endtabs %}

### Step 3) Faucet 接口[​](https://aptos.dev/tutorials/your-first-transaction#step-3-faucet-interface) <a href="#step-3-faucet-interface" id="step-3-faucet-interface"></a>

区块链水龙头为账户提供一定数量的代币，这些代币可用于支付 gas fees 或在用户之间代币转账。如果帐户尚不存在，Aptos 水龙头还可以创建一个帐户。Aptos 水龙头接口需要一个以十六进制编码字符串表示的公钥。[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface)

{% tabs %}
{% tab title="Python" %}

```python
class FaucetClient:
    """Faucet creates and funds accounts. This is a thin wrapper around that."""

    def __init__(self, url: str, rest_client: RestClient) -> None:
        self.url = url
        self.rest_client = rest_client

    def fund_account(self, pub_key: str, amount: int) -> None:
        """This creates an account if it does not exist and mints the specified amount of
        coins into that account."""

        txns = requests.post(f"{self.url}/mint?amount={amount}&pub_key={pub_key}")
        assert txns.status_code == 200, txns.text
        for txn_hash in txns.json():
            self.rest_client.wait_for_transaction(txn_hash)
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
pub struct FaucetClient {
    url: String,
    rest_client: RestClient,
}

impl FaucetClient {
    /// Faucet creates and funds accounts. This is a thin wrapper around that.
    pub fn new(url: String, rest_client: RestClient) -> Self {
        Self { url, rest_client }
    }

    /// This creates an account if it does not exist and mints the specified amount of coins into that account.
    pub fn fund_account(&self, pub_key: &str, amount: u64) {
        let res = reqwest::blocking::Client::new()
            .post(format!(
                "{}/mint?amount={}&pub_key={}",
                self.url, amount, pub_key
            ))
            .send()
            .unwrap();

        if res.status() != 200 {
            assert_eq!(
                res.status(),
                200,
                "{}",
                res.text().unwrap_or("".to_string()),
            );
        }
        for txn_hash in res.json::<serde_json::Value>().unwrap().as_array().unwrap() {
            self.rest_client
                .wait_for_transaction(txn_hash.as_str().unwrap())
        }
    }
}
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
/** Faucet creates and funds accounts. This is a thin wrapper around that. */
export class FaucetClient {
  url: string;
  restClient: RestClient;

  constructor(url: string, restClient: RestClient) {
    this.url = url;
    this.restClient = restClient;
  }

  /** This creates an account if it does not exist and mints the specified amount of
   coins into that account */
  async fundAccount(pubKey: string, amount: number) {
    const url = `${this.url}/mint?amount=${amount}&pub_key=${pubKey}`;
    const response = await fetch(url, {method: "POST"});
    if (response.status != 200) {
      assert(response.status == 200, await response.text());
    }
    const tnxHashes = await response.json() as Array<string>;
    for (const tnxHash of tnxHashes) {
      await this.restClient.waitForTransaction(tnxHash);
    }
  }

}
```

{% endtab %}
{% endtabs %}

### Step 4) 执行应用程序并验证[​](https://aptos.dev/tutorials/your-first-transaction#step-4-execute-the-application-and-verify)[​](https://aptos.dev/tutorials/your-first-transaction#step-2-rest-interface) <a href="#step-4-execute-the-application-and-verify" id="step-4-execute-the-application-and-verify"></a>

{% tabs %}
{% tab title="Python" %}

```python
if __name__ == "__main__":
    rest_client = RestClient(TESTNET_URL)
    faucet_client = FaucetClient(FAUCET_URL, rest_client)

    # Create two accounts, Alice and Bob, and fund Alice but not Bob
    alice = Account()
    bob = Account()

    print("\n=== Addresses ===")
    print(f"Alice: {alice.address()}")
    print(f"Bob: {bob.address()}")

    faucet_client.fund_account(alice.pub_key(), 1_000_000)
    faucet_client.fund_account(bob.pub_key(), 0)

    print("\n=== Initial Balances ===")
    print(f"Alice: {rest_client.account_balance(alice.address())}")
    print(f"Bob: {rest_client.account_balance(bob.address())}")

    # Have Alice give Bob 10 coins
    tx_hash = rest_client.transfer(alice, bob.address(), 1_000)
    rest_client.wait_for_transaction(tx_hash)

    print("\n=== Final Balances ===")
    print(f"Alice: {rest_client.account_balance(alice.address())}")
    print(f"Bob: {rest_client.account_balance(bob.address())}")
```

{% endtab %}

{% tab title="Rust" %}

```rust
fn main() -> () {
    let rest_client = RestClient::new(TESTNET_URL.to_string());
    let faucet_client = FaucetClient::new(FAUCET_URL.to_string(), rest_client.clone());

    // Create two accounts, Alice and Bob, and fund Alice but not Bob
    let mut alice = Account::new(None);
    let bob = Account::new(None);

    println!("\n=== Addresses ===");
    println!("Alice: 0x{}", alice.address());
    println!("Bob: 0x{}", bob.address());

    faucet_client.fund_account(&alice.pub_key(), 1_000_000);
    faucet_client.fund_account(&bob.pub_key(), 0);

    println!("\n=== Initial Balances ===");
    println!("Alice: {:?}", rest_client.account_balance(&alice.address()));
    println!("Bob: {:?}", rest_client.account_balance(&bob.address()));

    // Have Alice give Bob 10 coins
    let tx_hash = rest_client.transfer(&mut alice, &bob.address(), 1_000);
    rest_client.wait_for_transaction(&tx_hash);

    println!("\n=== Final Balances ===");
    println!("Alice: {:?}", rest_client.account_balance(&alice.address()));
    println!("Bob: {:?}", rest_client.account_balance(&bob.address()));
}
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
/** run our demo! */
async function main() {
  const restClient = new RestClient(TESTNET_URL);
  const faucetClient = new FaucetClient(FAUCET_URL, restClient);

  // Create two accounts, Alice and Bob, and fund Alice but not Bob
  const alice = new Account();
  const bob = new Account();

  console.log("\n=== Addresses ===");
  console.log(`Alice: ${alice.address()}. Key Seed: ${Buffer.from(alice.signingKey.secretKey).toString("hex").slice(0, 64)}`);
  console.log(`Bob: ${bob.address()}. Key Seed: ${Buffer.from(bob.signingKey.secretKey).toString("hex").slice(0, 64)}`);

  await faucetClient.fundAccount(alice.pubKey(), 1_000_000_000);
  await faucetClient.fundAccount(bob.pubKey(), 0);

  console.log("\n=== Initial Balances ===");
  console.log(`Alice: ${await restClient.accountBalance(alice.address())}`);
  console.log(`Bob: ${await restClient.accountBalance(bob.address())}`);

  // Have Alice give Bob 1000 coins
  const txHash = await restClient.transfer(alice, bob.address(), 1_000);
  await restClient.waitForTransaction(txHash);

  console.log("\n=== Final Balances ===");
  console.log(`Alice: ${await restClient.accountBalance(alice.address())}`);
  console.log(`Bob: ${await restClient.accountBalance(bob.address())}`);
}

if (require.main === module) {
  main().then((resp) => console.log(resp));
}
```

{% endtab %}
{% endtabs %}

执行后的结果输出:

```
=== Addresses ===
Alice: e26d69b8d3ff12874358da6a4082a2ac
Bob: c8585f009c8a90f22c6b603f28b9ed8c

=== Initial Balances ===
Alice: 1000000000
Bob: 0

=== Final Balances ===
Alice: 999998957
Bob: 1000
```

结果显示 Bob 从 Alice 那里收到了 1000 个硬币。 Alice 支付了 43 个硬币来支付 gas。 可以通过访问 REST 接口或区块浏览器来验证数据：

* Alice 的账户：[REST 接口](https://fullnode.devnet.aptoslabs.com/accounts/e26d69b8d3ff12874358da6a4082a2ac/resources)
* Bob 的账户：[区块浏览器](https://aptos-explorer.netlify.app/account/c8585f009c8a90f22c6b603f28b9ed8c)

{% hint style="info" %}
提醒：

devnet 会被定期重置，因此上面的链接可能无法正常查询。你可以自己尝试教程，然后在区块浏览器中查看他们的帐户。
{% endhint %}


# 您的第一个 Move 模块

本教程详细介绍了如何编写、编译、测试、发布以及与 Aptos 区块链上的 Move 模块交互。步骤如下：&#x20;

* 编写、编译和测试 Move 模块
* 将 Move 模块发布到 Aptos 区块链
* 初始化并与 Move 模块的资源交互

{% hint style="info" %}
请从下面的代码仓库中 clone 最新代码！
{% endhint %}

本教程以“[您的第一笔交易](/jiao-cheng/nin-de-di-yi-bi-jiao-yi)”为基础，并借用该代码作为本示例的库。 在下面下载示例代码：

{% tabs %}
{% tab title="Python" %}
对于本教程，将重点关注 **hello\_blockchain.py** 并重用上一教程中的 **first\_transaction.py** 库。&#x20;

你可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python)找到 python 项目。
{% endtab %}

{% tab title="Rust" %}
对于本教程，将重点关注 **hello\_blockchain.py** 并重用上一教程中的 **first\_transaction.py** 库。&#x20;

你可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/rust)找到 Rust 项目。
{% endtab %}

{% tab title="Typescript" %}
对于本教程，将重点关注 **hello\_blockchain.py** 并重用上一教程中的 **first\_transaction.py** 库。&#x20;

你可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript)找到 typescript 项目。
{% endtab %}
{% endtabs %}

### Step 1) 编写和测试 Move 模块[​](https://aptos.dev/tutorials/your-first-move-module#step-1-write-and-test-the-move-module) <a href="#step-1-write-and-test-the-move-module" id="step-1-write-and-test-the-move-module"></a>

#### Step 1.1) 下载 Aptos-core[​](https://aptos.dev/tutorials/your-first-move-module#step-11-download-aptos-core) <a href="#step-11-download-aptos-core" id="step-11-download-aptos-core"></a>

为了简化本练习，Aptos-core 有一个 `move-examples` 目录，可以轻松构建和测试 Move 模块，而无需下载其他资源。 随着时间的推移，我们将扩展本节以描述如何利用 Move 工具进行开发。

现在，下载并准备 Aptos-core：

```shell
git clone https://github.com/aptos-labs/aptos-core.git
cd aptos-core
./scripts/dev_setup.sh
source ~/.cargo/env
```

#### Step 1.2) 查看模块[​](https://aptos.dev/tutorials/your-first-move-module#step-12-review-the-module) <a href="#step-12-review-the-module" id="step-12-review-the-module"></a>

在上面的终端窗口中，将目录更改为 `aptos-move/move-examples`。 在本教程的其余部分保留此终端窗口 - 后面我们将它称为“Move 窗口”。 本节的其余部分将查看文件 \``sources/HelloBlockchain.move`\`。

该模块使用户可以在其帐户下创建 \`String\` 资源并进行设置。 用户只能设置自己的资源，不能设置他人的资源。

```rust
module HelloBlockchain::Message {
    use Std::ASCII;
    use Std::Errors;
    use Std::Signer;

    struct MessageHolder has key {
        message: ASCII::String,
    }

    public(script) fun set_message(account: signer, message_bytes: vector<u8>)
    acquires MessageHolder {
        let message = ASCII::string(message_bytes);
        let account_addr = Signer::address_of(&account);
        if (!exists<MessageHolder>(account_addr)) {
            move_to(&account, MessageHolder {
                message,
            })
        } else {
            let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
            old_message_holder.message = message;
        }
    }
}
```

在前面的代码中，两个重要的部分是 `MessageHolder` 结构体和函数 `set_message`。 `set_message` 是一个 `script` 函数，允许事务直接调用它。调用它时，该函数将确定当前帐户是否具有 `MessageHolder` 资源，如果不存在，则创建并存储该 `message`。 如果资源存在，则覆盖 `MessageHolder` 中的 `message`。

#### Step 1.3) 测试模块[​](https://aptos.dev/tutorials/your-first-move-module#step-13-testing-the-module) <a href="#step-13-testing-the-module" id="step-13-testing-the-module"></a>

Move 允许内联测试，因此我们添加了 `get_message` 以方便获取 `message`，并添加了一个测试方法 `sender_can_set_message` 来验证端到端流。 这可以通过运行 `cargo test` 来验证。 在 `sources/HelloBlockchainTest.move` 下还有另一个测试，演示了另一种编写测试的方法。

注意：`sender_can_set_message` 是一个 `script` 函数，以便调用脚本函数 `set_message`。

```rust
    const ENO_MESSAGE: u64 = 0;

    public fun get_message(addr: address): ASCII::String acquires MessageHolder {
        assert!(exists<MessageHolder>(addr), Errors::not_published(ENO_MESSAGE));
        *&borrow_global<MessageHolder>(addr).message
    }

    #[test(account = @0x1)]
    public(script) fun sender_can_set_message(account: signer) acquires MessageHolder {
        let addr = Signer::address_of(&account);
        set_message(account,  b"Hello, Blockchain");

        assert!(
          get_message(addr) == ASCII::string(b"Hello, Blockchain"),
          0
        );
    }
```

### Step 2) 发布并与 Move 模块交互[​](https://aptos.dev/tutorials/your-first-move-module#step-2-publishing-and-interacting-with-the-move-module) <a href="#step-2-publishing-and-interacting-with-the-move-module" id="step-2-publishing-and-interacting-with-the-move-module"></a>

现在我们回到我们的应用程序来部署 Atpos 区块链上的模块并与之交互。 如前所述，本教程建立在早期教程的基础上并共享通用代码。 因此，本教程仅讨论该库的新功能，包括发布、发送 `set_message` 交易和读取 `MessageHolder::message` 的能力。 发布模块和提交交易的唯一区别是负载（payload）类型。 请参阅以下内容：

#### Step 2.1) 发布  Move 模块[​](https://aptos.dev/tutorials/your-first-move-module#step-21-publishing-the-move-module) <a href="#step-21-publishing-the-move-module" id="step-21-publishing-the-move-module"></a>

{% tabs %}
{% tab title="Python" %}

```python
class HelloBlockchainClient(RestClient):

    def publish_module(self, account_from: Account, module_hex: str) -> str:
        """Publish a new module to the blockchain within the specified account"""

        payload = {
            "type": "module_bundle_payload",
            "modules": [
                {"bytecode": f"0x{module_hex}"},
            ],
        }
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        res = self.submit_transaction(signed_txn)
        return str(res["hash"])
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
pub struct HelloBlockchainClient {
    pub rest_client: RestClient,
}

impl HelloBlockchainClient {
    /// Represents an account as well as the private, public key-pair for the Aptos blockchain.
    pub fn new(url: String) -> Self {
        Self {
            rest_client: RestClient::new(url),
        }
    }

    /// Publish a new module to the blockchain within the specified account
    pub fn publish_module(&self, account_from: &mut Account, module_hex: &str) -> String {
        let payload = serde_json::json!({
            "type": "module_bundle_payload",
            "modules": [{"bytecode": format!("0x{}", module_hex)}],
        });
        let txn_request = self
            .rest_client
            .generate_transaction(&account_from.address(), payload);
        let signed_txn = self.rest_client.sign_transaction(account_from, txn_request);
        let res = self.rest_client.submit_transaction(&signed_txn);
        res.get("hash").unwrap().as_str().unwrap().to_string()
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
class HelloBlockchainClient extends RestClient {

  /** Publish a new module to the blockchain within the specified account */
  async publishModule(accountFrom: Account, moduleHex: string): Promise<string> {
    const payload = {
      "type": "module_bundle_payload",
      "modules": [
        {"bytecode": `0x${moduleHex}`},
      ],
    };
    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(accountFrom, signedTxn);
    return res["hash"];
  }
  
```

{% endtab %}
{% endtabs %}

#### Step 2.2) 读取资源[​](https://aptos.dev/tutorials/your-first-move-module#step-22-reading-a-resource) <a href="#step-22-reading-a-resource" id="step-22-reading-a-resource"></a>

该模块在一个地址发布。 这是下面的 `contract_address`。 这类似于前面的示例，其中 `TestCoin` 在 `0x1`地址下。 `contract_address` 将与发布它的地址相同。[​](https://aptos.dev/tutorials/your-first-move-module#step-21-publishing-the-move-module)

{% tabs %}
{% tab title="Python" %}

```python
    def get_message(self, contract_address: str, account_address: str) -> Optional[str]:
        """ Retrieve the resource Message::MessageHolder::message """

        resources = self.account_resources(account_address)
        for resource in resources:
            if resource["type"] == f"0x{contract_address}::Message::MessageHolder":
                return resource["data"]["message"]
        return None
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
    /// Retrieve the resource Message::MessageHolder::message
    pub fn get_message(&self, contract_address: &str, account_address: &str) -> Option<String> {
        let module_type = format!("0x{}::Message::MessageHolder", contract_address);
        self.rest_client
            .account_resources(account_address)
            .as_array()
            .unwrap()
            .iter()
            .find(|x| {
                x.get("type")
                    .map(|v| v.as_str().unwrap() == module_type)
                    .unwrap_or(false)
            })
            .and_then(|coin| {
                coin.get("data")
                    .unwrap()
                    .get("message")
                    .unwrap()
                    .as_str()
                    .and_then(|s| Some(s.to_string()))
            })
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
  /** Retrieve the resource Message::MessageHolder::message */
  async getMessage(contractAddress: string, accountAddress: string): Promise<string> {
    const resources = await this.accountResources(accountAddress);
    for (const key in resources) {
      const resource = resources[key];
      if (resource["type"] == `0x${contractAddress}::Message::MessageHolder`) {
        return resource["data"]["message"];
      }
    }
  }
  
```

{% endtab %}
{% endtabs %}

#### Step 2.3) 修改资源[​](https://aptos.dev/tutorials/your-first-move-module#step-23-modifying-a-resource) <a href="#step-23-modifying-a-resource" id="step-23-modifying-a-resource"></a>

Move 模块必须公开用于初始化和操作资源的 `script` 函数。 然后可以从交易中调用该 `script`。

注意：虽然 REST 接口可以显示字符串，但由于 JSON 和 Move 的限制，它无法确定参数是字符串还是十六进制编码的字符串。 所以交易参数总是假设后者。 因此，在此示例中，消息被编码为十六进制字符串。[​](https://aptos.dev/tutorials/your-first-move-module#step-21-publishing-the-move-module)

{% tabs %}
{% tab title="Python" %}

```python
    def set_message(self, contract_address: str, account_from: Account, message: str) -> str:
        """ Potentially initialize and set the resource Message::MessageHolder::message """

        payload = {
            "type": "script_function_payload",
            "function": f"0x{contract_address}::Message::set_message",
            "type_arguments": [],
            "arguments": [
                message.encode("utf-8").hex(),
            ]
        }
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        res = self.submit_transaction(signed_txn).json()
        return str(res["hash"])
    
```

{% endtab %}

{% tab title="Rust" %}

```rust
    /// Potentially initialize and set the resource Message::MessageHolder::message
    pub fn set_message(
        &self,
        contract_address: &str,
        account_from: &mut Account,
        message: &str,
    ) -> String {
        let message_hex = hex::encode(message.as_bytes());
        let payload = serde_json::json!({
            "type": "script_function_payload",
            "function": format!("0x{}::Message::set_message", contract_address),
            "type_arguments": [],
            "arguments": [message_hex]
        });

        let txn_request = self
            .rest_client
            .generate_transaction(&account_from.address(), payload);
        let signed_txn = self.rest_client.sign_transaction(account_from, txn_request);
        self.rest_client
            .submit_transaction(&signed_txn)
            .get("hash")
            .unwrap()
            .as_str()
            .unwrap()
            .to_string()
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```typescript
  /**  Potentially initialize and set the resource Message::MessageHolder::message */
  async setMessage(contractAddress: string, accountFrom: Account, message: string): Promise<string> {
    let payload: { function: string; arguments: string[]; type: string; type_arguments: any[] };
    payload = {
      "type": "script_function_payload",
      "function": `0x${contractAddress}::Message::set_message`,
      "type_arguments": [],
      "arguments": [
        Buffer.from(message, "utf-8").toString("hex")
      ]
    };

    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(accountFrom, signedTxn);
    return res["hash"];
  }

}
```

{% endtab %}
{% endtabs %}

#### Step 3) 初始化并与 Move 模块交互[​](https://aptos.dev/tutorials/your-first-move-module#step-3-initialize-and-interact-with-the-move-module) <a href="#step-3-initialize-and-interact-with-the-move-module" id="step-3-initialize-and-interact-with-the-move-module"></a>

{% tabs %}
{% tab title="Python" %}
For Python3:

* 下载[示例项目](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python)
* 打开您喜欢的终端并导航到您下载上述示例项目的位置
* 安装要求的依赖包：`pip3 install -r requirements.txt`.
* 执行示例命令：`python3 hello_blockchain.py Message.mv`
* 片刻之后，它会输出 "Update the module with Alice's address, build, copy to the provided path, and press enter."
* 在 "Move 窗口" 的终端中，对于我们之前看过的 Move 文件：
  * 编辑 `Move.toml` 并在 `[addresses]` 下将 `0xe110` 地址替换为 Alice 的地址，在提示上方输出。
  * 构建 `cargo run -- sources`
  * 拷贝 `build/Examples/bytecode_modules/Message.mv` 到与本教程项目代码相同的文件夹中
* 返回到您的另一个终端窗口，并在提示符处按下“Enter”键以继续执行其余代码
  {% endtab %}

{% tab title="Rust" %}

{% endtab %}

{% tab title="Typescript" %}

{% endtab %}
{% endtabs %}

输出的结果如下：

```
=== Addresses ===
Alice: a52671f10dc3479b09d0a11ce47694c0
Bob: ec6ec14e4abe10aaa6ad53b0b63a1806

=== Initial Balances ===
Alice: 10000000
Bob: 10000000

Update the module with Alice's address, build, copy to the provided path, and press enter.

=== Testing Alice ===
Publishing...
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain

=== Testing Bob ===
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain
```

结果表明，Alice 和 Bob 从没有资源变成了一个 `message` 设置为“Hello, Blockchain”的地址。

可以通过访问 REST 接口或区块浏览器来验证数据：

* Alice 的账户：[REST 接口](https://fullnode.devnet.aptoslabs.com/accounts/a52671f10dc3479b09d0a11ce47694c0/)
* Bob 的账户：[区块浏览器](https://explorer.devnet.aptos.dev/account/ec6ec14e4abe10aaa6ad53b0b63a1806)


# 您的第一个 NFT

{% hint style="info" %}
注意：以下教程正在进行中。 此外，Aptos（非同质化）代币规范尚未正式确定。
{% endhint %}

## Aptos 中的代币和 NFT

[NFT](https://en.wikipedia.org/wiki/Non-fungible_token) 是一种不可替代的代币或存储在区块链上的数据，它唯一地定义了资产的所有权。 NFT 最初是在 [EIP-721](https://eips.ethereum.org/EIPS/eip-721) 中定义的，后来在 [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155) 中进行了扩展。 NFT 通常包括以下几个方面：

* 名称，资产的名称，在集合中必须是唯一的
* 描述，资产的描述
* URL，一个指向关于资产的更多信息的链外非描述指针可以是媒体，如图像或视频或更多元数据
* 供应量，这个 NFT 的单位总数，许多 NFT 只有一个，而那些有多个的被称为版本

此外，大多数 NFT 是具有共同属性（例如主题、创建者或最小合约）的集合或一组 NFT 的一部分。每个集合都有一组相似的属性：

* 名称，集合的名称，在创建者的帐户中必须是唯一的
* 描述，资产的描述
* URL，一个指向关于资产的更多信息的链外非描述指针可以是媒体，如图像或视频或更多元数据

核心 NFT 或代币的 Aptos 实现可以在 [Token.move](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/Token.move) 中找到。

### Aptos Token 定义[​](https://aptos.dev/tutorials/your-first-nft#aptos-token-definitions) <a href="#aptos-token-definitions" id="aptos-token-definitions"></a>

#### Token[​](https://aptos.dev/tutorials/your-first-nft#the-token) <a href="#the-token" id="the-token"></a>

Aptos Token 被定义为：

| 域            | 类型              | 描述                                        |
| ------------ | --------------- | ----------------------------------------- |
| `id`         | `GUID:ID`       | 代币的全局唯一标识符也可用于识别创建者                       |
| `name`       | `ASCII::String` | 代币的名称，在集合中必须是唯一的                          |
| `collection` | `GUID:ID`       | 包含此代币的集合的全局唯一标识符                          |
| `balance`    | `u64`           | 与供应相关的此代币的当前存储量, `1 <= balance <= supply` |

Aptos TokenData 被定义为：

| 域             | 类型              | 描述                               |
| ------------- | --------------- | -------------------------------- |
| `id`          | `GUID:ID`       | 代币的全局唯一标识符也可用于识别创建者              |
| `description` | `ASCII::String` | 代币描述信息                           |
| `name`        | `ASCII::String` | 代币的名称，在集合中必须是唯一的                 |
| `supply`      | `u64`           | 此 Token 的总版本数                    |
| `uri`         | `ASCII::String` | 附加信息/媒体的 URL                     |
| `metadata`    | `TokenType`     | 一个通用的、可选的用户定义结构，用于包含有关此代币链上的附加信息 |

Token 是使用 Move 属性  `store` 定义的，这意味着它们可以保存到全局存储中。Token 不能被隐式丢弃，必须被销毁以确保总余额等于供应。Token 不能被复制。也就是说，由于缺少复制操作，除了创建者之外，任何人都无法更改总余额或供应量。请注意，当前的 API 没有公开发布创建代币的能力。Token 可以通过其 id 或 TokenType、集合名称和 Token 名称的元组来唯一标识。

TokenData 具有  `copy`  属性，支持简单的代币余额拆分。每当余额大于 1 的个人向另一个人提供其余额的一部分少于其总余额时，就会发生代币拆分。交易代币的用户请注意，两个代币可以共享相同的 TokenData，因为 Aptos 标准不会尝试识别代币是否复制了另一个代币的属性。重复前面所说的，代币可以通过其 id 或 TokenType、集合名称和代币名称来唯一标识。创建者可以更改 TokenType、集合名称或代币名称集中的任何值，以创建相似但不完全相同的代币。

#### Token[​](https://aptos.dev/tutorials/your-first-nft#the-token-collection) Collections <a href="#the-token-collection" id="the-token-collection"></a>

Aptos 定义了一组按其唯一 ID 分组在一起的集合：

```rust
struct Collections<TokenType: copy + drop + store> has key {
    collections: Table<ASCII::String, Collection>,
}

struct TokenMetadata<TokenType: store> has key {
    metadata: Table<ID, TokenType>,
}
```

由于 Collections 具有属性键，因此它直接存储到创建者帐户。 需要注意的是，如果没有 Collections 的概念，而 Collection 具有 key 属性，则 Aptos 帐户只能有一个集合，而通常情况并非如此。 可以按名称在集合集中查找集合，因此强制使用唯一的集合名称。

Token 和 TokenData 结构在其内容中是固定的。 资源 TokenMetadata 使创建者能够存储额外的 Token 数据。 表中的数据存储为 Token 的唯一 ID。 由于 script 函数不能支持结构或泛型的限制，使用它是可选的并且需要定制化 API。

每个集合都有以下字段：

| 域                | 类型                                               | 描述                                 |
| ---------------- | ------------------------------------------------ | ---------------------------------- |
| `tokens`         | `Table<ASCII::String, TokenMetadata<TokenType>>` | 跟踪与此集合关联的所有Token                   |
| `claimed_tokens` | `Table<ASCII::String, address>`                  | 跟踪 supply == 1 的 Token 的存储地址       |
| `description`    | `ASCII::String`                                  | 集合描述                               |
| `name`           | `ASCII::String`                                  | 此集合的名称在指定 TokenType 的创建者帐户中必须是唯一的。 |
| `uri`            | `ASCII::String`                                  | 附加信息/媒体的 URL                       |
| `count`          | `u64`                                            | 此集合跟踪的不同 Token 总数                  |
| `maximum`        | `Option<u64>`                                    | 可在此集合中铸造的可选 Token 的最大数量            |

集合不是用于累积 Token 的存储，因此它不包含 Token，而是包含 TokenData：

| 域      | 类型          | 描述                        |
| ------ | ----------- | ------------------------- |
| `id`   | `GUID:ID`   | 此 Token 的全球唯一标识符也可用于识别创建者 |
| `data` | `TokenData` | 关于此代币的附加数据，这是代币的供应量 > 1   |

#### Token 存储[​](https://aptos.dev/tutorials/your-first-nft#storing-tokens) <a href="#storing-tokens" id="storing-tokens"></a>

为了获取和存储 Token，用户必须拥有一个 TokenType 库：

```rust
struct Gallery has key {
    gallery: Table<ID, Token>,
}
```

与 Collections 一样，它作为资源存储在 Aptos 帐户上。

### Token 介绍[​](https://aptos.dev/tutorials/your-first-nft#introducing-tokens) <a href="#introducing-tokens" id="introducing-tokens"></a>

作为我们核心框架的一部分，Aptos 提供了一个基本的 [Token](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/Token.move) 接口，没有额外的数据，或者明确的 TokenMetadata 资源没有该令牌的条目。 这样做的动机包括：

* 创建新 Token 需要编写 Move 代码
* 用于创建新标记的脚本函数必须定制化，因为 Move 不支持模板类型或结构作为输入参数
* 脚本函数上的模板类型为编写脚本函数增加了额外的难度

本教程将引导您完成：

* 创建自己的 Token 集合
* 我们最喜欢的猫的 Token
* 并将该 Token 给其他人。

{% hint style="info" %}
请从下面的代码仓库中 clone 最新代码！
{% endhint %}

本教程以您的第一个事务为基础，作为此示例的库。 以下教程包含可以在下面完整下载的示例代码：

{% tabs %}
{% tab title="Python" %}
对于本教程，将重点关注 first\_nft.py 并重新使用上一教程中的 first\_transaction.py 库。 你可以在[这里](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python)找到 python 项目
{% endtab %}

{% tab title="Rust" %}
TODO
{% endtab %}

{% tab title="Typescript" %}
TODO
{% endtab %}
{% endtabs %}

#### 创建一个集合

Aptos Token 使创作者能够创建有限或无限的收藏。 许多 NFT 具有有限的性质，创造者只打算永远创造一定数量，这会导致稀缺性。 而其他代币可能具有无限的性质，例如，用于实用程序的集合可能会随着时间的推移出现新的代币。 SimpleToken 集合可以通过使用适当的脚本函数以任一行为实例化：

有限，即不能超过最大数量的代币可以铸造：

```rust
public(script) fun create_finite_collection_script(
    account: signer,
    description: vector<u8>,
    name: vector<u8>,
    uri: vector<u8>,
    maximum: u64,
)
```

无限制，即可以添加到集合中的 Token 数量没有限制：

```rust
public(script) fun create_unlimited_collection_script(
    account: signer,
    description: vector<u8>,
    name: vector<u8>,
    uri: vector<u8>,
)
```

这些脚本函数可以通过 REST API 调用。 下面演示了如何调用 ，如下所示：

{% tabs %}
{% tab title="Python" %}

```python
    def create_collection(self, account: Account, description: str, name: str, uri: str):
        """Creates a new collection within the specified account"""

        payload = {
            "type": "script_function_payload",
            "function": f"0x1::Token::create_unlimited_collection_script",
            "type_arguments": [],
            "arguments": [
                description.encode("utf-8").hex(),
                name.encode("utf-8").hex(),
                uri.encode("utf-8").hex(),
            ]
        }
        self.submit_transaction_helper(account, payload)
```

{% endtab %}
{% endtabs %}

#### 创建 Token[​](https://aptos.dev/tutorials/your-first-nft#creating-a-token) <a href="#creating-a-token" id="creating-a-token"></a>

可以在集合创建后创建 Token。 为此，Token 必须指定与先前创建的集合名称相同的 collection\_name。 创建 SimpleToken 的 Move 脚本函数是：

```rust
public(script) fun create_token_script(
    account: signer,
    collection_name: vector<u8>,
    description: vector<u8>,
    name: vector<u8>,
    supply: u64,
    uri: vector<u8>,
)
```

这些脚本函数可以通过 REST API 调用。 下面演示了如何调用 ，如下所示：

{% tabs %}
{% tab title="Python" %}

```python
    def create_token(
            self,
            account: Account,
            collection_name: str,
            description: str,
            name: str,
            supply: int,
            uri: str,
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::Token::create_token_script",
            "type_arguments": [],
            "arguments": [
                collection_name.encode("utf-8").hex(),    
                description.encode("utf-8").hex(),
                name.encode("utf-8").hex(),
                str(supply),
                uri.encode("utf-8").hex(),
            ]
        }
        self.submit_transaction_helper(account, payload)
```

{% endtab %}
{% endtabs %}

#### 发送 Token[​](https://aptos.dev/tutorials/your-first-nft#giving-away-a-token) <a href="#giving-away-a-token" id="giving-away-a-token"></a>

在 Aptos 和 Move 中，每个代币都占据空间并拥有所有权。 因此，代币转移不是单方面的，需要类似于公告板的两个阶段过程。 发送者必须首先注册一个 Token 可供接收者声明，然后接收者必须声明这个 Token。 这已在名为 [`TokenTransfer`](https://github.com/aptos-labs/aptos-core/blob/nft/aptos-move/framework/aptos-framework/sources/TokenTransfers.move) 的概念验证 Move 模块中实现。 SimpleToken 提供了一些包装函数来支持转账到另一个帐户、声明该转账或停止该转账。

**获得 Token ID**[**​**](https://aptos.dev/tutorials/your-first-nft#obtaining-the-token-id)

为了发送代币，发送者必须首先基于知道创建者的账户、集合名称和代币名称来识别代币id。 这可以通过查询 REST 获得：

{% tabs %}
{% tab title="Python" %}

```python
    def get_token_id(self, creator: str, collection_name: str, token_name: str) -> int:
        """ Retrieve the token's creation_num, which is useful for non-creator operations """

        resources = self.account_resources(creator)
        collections = []
        tokens = []
        for resource in resources:
            if resource["type"] == f"0x1::Token::Collections":
                collections = resource["data"]["collections"]["data"]
        for collection in collections:
            if collection["key"] == collection_name:
                tokens = collection["value"]["tokens"]["data"]
        for token in tokens:
            if token["key"] == token_name:
                return int(token["value"]["id"]["creation_num"])
            
        assert False
```

{% endtab %}
{% endtabs %}

**提供 Token**[**​**](https://aptos.dev/tutorials/your-first-nft#offering-the-token)

Token 中的以下 Move 脚本功能支持将 Token 转移到另一个帐户，有效地注册另一个帐户可以获取该 Token：

```rust
public(script) fun offer_script(
    sender: signer,
    receiver: address,
    creator: address,
    token_creation_num: u64,
    amount: u64,
)
```

{% tabs %}
{% tab title="Python" %}

```python
    def offer_token(
            self,
            account: Account,
            receiver: str,
            creator: str,
            token_creation_num: int,
            amount: int
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::TokenTransfers::offer_script",
            "type_arguments": [],
            "arguments": [
                receiver,
                creator,
                str(token_creation_num),
                str(amount),
            ]
        }
        self.submit_transaction_helper(account, payload)
```

{% endtab %}
{% endtabs %}

**领取 Token**[**​**](https://aptos.dev/tutorials/your-first-nft#claiming-the-token)

SimpleToken 中的以下 Move 脚本函数支持接收上一个函数提供的 Token，有效地声明 Token：

```rust
public(script) fun claim_script(
    sender: signer,
    receiver: address,
    creator: address,
    token_creation_num: u64,
    amount: u64,
)
```

{% tabs %}
{% tab title="Python" %}

```python
    def claim_token(
            self,
            account: Account,
            sender: str,
            creator: str,
            token_creation_num: int,
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::TokenTransfers::claim_script",
            "type_arguments": [],
            "arguments": [
                sender,
                creator,
                str(token_creation_num),
            ]
        }
        self.submit_transaction_helper(account, payload)
```

{% endtab %}
{% endtabs %}

### 待办 <a href="#todos-for-tokens" id="todos-for-tokens"></a>

* 添加额外的铸造能力
* 确保在铸造时至少产生一个代币
* 添加事件——需要关于什么事件的反馈
* 为 Token 提供可变 API
* 直接为泛型和简单 Token 写一个冒烟测试
* 以安全的方式启用燃烧


# 运行本地网络

您可以运行本地测试网来对 Aptos 区块链进行测试和开发。该网络独立于 Aptos 生态系统运行，仅用于测试和开发。

> 您的本地测试网将不会连接 Aptos 开发网（devnet）

### 开始[​](https://aptos.dev/tutorials/run-a-local-testnet#getting-started) <a href="#getting-started" id="getting-started"></a>

您可以通过两种方式运行本地测试网：使用 Aptos-core 源码或 Docker。

* Aptos-core 源码：在对 Aptos-core 代码库或 Aptos 框架的修改测试时使用。
* Docker：在 Aptos 区块链之上构建服务或在 Aptos 框架上构建应用程序时使用。因为没有构建开销，并且默认情况下，账本状态在网络重新启动时保持不变。

#### 使用 Aptos-core 源码[​](https://aptos.dev/tutorials/run-a-local-testnet#using-aptos-core-source-code) <a href="#using-aptos-core-source-code" id="using-aptos-core-source-code"></a>

1. 从 GitHub 下载并克隆 Aptos-core 项目，并通过运行以下命令准备您的开发环境：

   ```
   git clone https://github.com/aptos-labs/aptos-core.git
   cd aptos
   ./scripts/dev_setup.sh
   source ~/.cargo/env
   ```
2. 运行进程： `cargo run -p aptos-node -- --test`. 启动后，该进程会打印他的配置路径（例如, `/private/var/folders/36/w0v54r116ls44q29wh8db0mh0000gn/T/f62a72f87940e3892a860c21b55b529b/0/node.yaml`）和其他元数据。

注意： 该命令仅从创世账本状态运行 `aptos-node`。如果你想重用之前运行 `aptos-node` 产生的账本，使用 `cargo run -p aptos-node -- --test --config <config-path>`.

#### 使用 Docker[​](https://aptos.dev/tutorials/run-a-local-testnet#using-docker) <a href="#using-docker" id="using-docker"></a>

1. 安装 Docker 和 Docker-Compose。
2. 为你个本地测试验证者网络创建一个目录。
3. 下载 [validator testnet docker compose](https://github.com/aptos-labs/aptos-core/blob/main/docker/compose/validator-testnet/docker-compose.yaml) 和 [validator configuration](https://github.com/aptos-labs/aptos-core/blob/main/docker/compose/validator-testnet/validator_node_template.yaml).
4. 在同一目录下创建配置文件，以便将数据导出到 docker 容器中：

   ```
   # Linux / Mac
   touch genesis.blob aptos_root_key waypoint.txt

   # Windows
   fsutil file createnew genesis.blob 0
   fsutil file createnew aptos_root_key 0
   fsutil file createnew waypoint.txt 0
   Run docker-compose: docker-compose up
   ```

### 和本地测试验证者网络交互[​](https://aptos.dev/tutorials/run-a-local-testnet#interacting-with-the-local-test-validator-network) <a href="#interacting-with-the-local-test-validator-network" id="interacting-with-the-local-test-validator-network"></a>

启动本地测试验证者网络后，您应该会看到以下内容：

```
validator_1  | Entering test mode, this should never be used in production!
validator_1  | Completed generating configuration:
validator_1  |  Log file: "/opt/aptos/var/validator.log"
validator_1  |  Config path: "/opt/aptos/var/0/node.yaml"
validator_1  |  Aptos root key path: "/opt/aptos/var/mint.key"
validator_1  |  Waypoint: 0:7ff525d33f685a5cf26a71b393fa5159874c8f0c2861c382905f49dcb6991cb6
validator_1  |  REST endpoint: 0.0.0.0:8080
validator_1  |  FullNode network: /ip4/0.0.0.0/tcp/7180
validator_1  |  ChainId: TESTING
```

此输出包含启动 Aptos CLI（命令行）工具所需的信息：

* Aptos root key path - 根（也称为铸币 `mint` 或水龙头 `faucet`）密钥控制可以铸币的帐户。 在  docker compose 文件夹中的 `aptos_root_key` 下。
* Waypoint - 区块链中可验证的检查点（在 docker compose 文件夹中的 waypoint.txt 下）
* REST endpoint - `http://127.0.0.1:8080`.
* ChainId - 链 ID，将这条链与其他链区分开。

### 接下来的步骤[​](https://aptos.dev/tutorials/run-a-local-testnet#next-steps) <a href="#next-steps" id="next-steps"></a>

此时，您将在 `0x1` 拥有一个可以执行铸币操作的特殊 root 帐户。

* 如何提交交易：[您的第一笔交易](/jiao-cheng/nin-de-di-yi-bi-jiao-yi)
* 如何创建 Move 模块：[您的第一个 Move 模块](/jiao-cheng/nin-de-di-yi-ge-move-mo-kuai)
* 铸币：[Interacting with the Aptos Blockchain](https://aptos.dev/transactions/interacting-with-the-aptos-blockchain)

需要注意的是，这些方案不包括水龙头。 这留给读者作为练习。


# 开发网 Devnet


# 运行全节点（官方教程）

开发网运行全节点教程（非测试网）

您可以运行 [FullNodes](/ji-chu/quan-jie-dian) 来验证状态并同步到 Aptos 区块链。 FullNodes 可以由任何人运行。 FullNodes 通过相互查询或直接查询验证者来复制区块链的完整状态。

本教程详细介绍了如何配置公共 FullNode 以连接到 Aptos 开发网。 Aptos Labs 提供的 FullNode 具有速率限制，可能会阻碍在测试网上的开发。 运行全节点将直接为您提供数据以避免此类速率限制。&#x20;

> **注意：**&#x60A8;的公共 FullNode 将通过 REST 端点连接到 devnet，可在您的计算机上的 localhost:8080 来访问该端点。

## **前提条件**[**​**](https://aptos.dev/tutorials/run-a-fullnode#prerequisites)

在开始学习本教程之前，我们建议您熟悉以下内容：

* [验证者节点概念](/ji-chu/yan-zheng-zhe-jie-dian)
* [全节点概念](/ji-chu/quan-jie-dian)
* [REST 规范](https://github.com/aptos-labs/aptos-core/tree/main/api)

### 硬件要求[​](https://aptos.dev/tutorials/run-a-fullnode#hardware-requirement) <a href="#hardware-requirement" id="hardware-requirement"></a>

为了运行生产级的全节点，我们建议使用以下硬件：

* CPU: Intel Xeon Skylake 或更新，4 核
* 内存：8GiB RAM

如果运行全节点用于开发或测试目的：

* CPU: 2 核
* 内存：4GiB RAM

### 存储要求

注意：Aptos 存储的数据量取决于区块链的账本历史（长度）和链上状态（例如账户）的数量。 这可能受到许多因素的影响，包括：区块链的年龄、平均交易率和账本修剪器的配置。

鉴于 DevNet 目前每周都会重置（请参阅 Discord 上的 #devnet-release 频道），我们估计 Aptos 需要的存储空间不会超过几 GB。

## 开始[​](https://aptos.dev/tutorials/run-a-fullnode#getting-started) <a href="#getting-started" id="getting-started"></a>

您可以通过两种方式配置公共 FullNode：使用 Aptos-core 源码或 Docker。

### 使用 Aptos-core 源码[​](https://aptos.dev/tutorials/run-a-fullnode#using-aptos-core-source-code) <a href="#using-aptos-core-source-code" id="using-aptos-core-source-code"></a>

1. 从 GitHub 下载并克隆 Aptos-core 项目，并通过运行以下命令准备您的开发环境：

   ```
   git clone https://github.com/aptos-labs/aptos-core.git
   cd aptos-core
   ./scripts/dev_setup.sh
   source ~/.cargo/env
   ```
2. 检出 devnet 分支：`git checkout origin/devnet`
3. 准备你的配置文件：
   * 将 `config/src/config/test_data/public_full_node.yaml`拷贝到你的工作目录。
   * 下载 devnet 的 [genesis](https://devnet.aptoslabs.com/genesis.blob) 和 [waypoint](https://devnet.aptoslabs.com/waypoint.txt) 文件。
   * 在你当前的工作目录中更新 public\_full\_node.yaml 文件：
     * 指定你想存储 devet 的数据目录到 `base:data_dir`；例如： `./data`。
     * 拷贝和粘贴 waypoint 文件内容到 `waypoint` 区域。
     * 通读配置并进行任何其他所需的更改。 您可以通过检查以下文件作为示例来查看 `public_full_node.yaml` 文件应该具有哪些配置： `docker/compose/public_full_node/public_full_node.yaml`
4. 运行 aptos-node：`cargo run -p aptos-node --release -- -f ./public_full_node.yaml`

您现在已成功配置并开始运行连接到 Aptos devnet 的全节点。

注意：这将在 `target/release/aptos-node` 下构建一个发布二进制文件。发布二进制文件往往比调试二进制文件快得多，但缺少对开发有用的调试信息。 只需省略 `--release` 标志即可构建调试二进制文件。

### 使用 Docker[​](https://aptos.dev/tutorials/run-a-fullnode#using-docker) <a href="#using-docker" id="using-docker"></a>

您还可以使用 Docker 来配置和运行您的全节点。

1. 安装 Docker 和 Docker-Compose。
2. 为您的公共全节点创建一个目录。
3. 将公共全节点的 [docker compose](https://github.com/aptos-labs/aptos-core/tree/main/docker/compose/public_full_node/docker-compose.yaml) 和 [aptos-core](https://github.com/aptos-labs/aptos-core/tree/main/docker/compose/public_full_node/public_full_node.yaml) 配置文件下载到此目录中。
4. 将 devnet 的 [genesis](https://devnet.aptoslabs.com/genesis.blob) 和 [waypoint](https://devnet.aptoslabs.com/waypoint.txt) 文件下载到该目录中。
5. 运行 docker-compose： `docker-compose up`.

## 验证您的全节点的正确性[​](https://aptos.dev/tutorials/run-a-fullnode#understand-and-verify-the-correctness-of-your-fullnode) <a href="#understand-and-verify-the-correctness-of-your-fullnode" id="understand-and-verify-the-correctness-of-your-fullnode"></a>

### **验证初始化同步**[**​**](https://aptos.dev/tutorials/run-a-fullnode#initial-synchronization)

在您的 FullNode 的初始同步期间，可能需要传输大量数据。 可以通过查询指标端口来监控进度，以查看您的节点当前同步到的版本。 运行以下命令：

```
curl 127.0.0.1:9101/metrics 2> /dev/null | grep "aptos_state_sync_version{.*\"synced\"}" | awk '{print $2}'
```

该命令将输出节点的当前同步版本。 例如：

```
71000
```

将此命令返回的同步版本（例如 71000）与 [Aptos 状态页面](https://status.devnet.aptos.dev/)上显示的当前版本（最新）进行比较。 如果您的节点正在赶上当前版本，则它正在正确同步。 注意：节点和状态页面之间存在一些版本差异是可以的，因为状态页面不会自动刷新。

### （可选）验证出站网络连接​[​](https://aptos.dev/tutorials/run-a-fullnode#optional-verify-outbound-network-connections) <a href="#optional-verify-outbound-network-connections" id="optional-verify-outbound-network-connections"></a>

出站网络连接数应大于0。运行以下命令：

```
curl 127.0.0.1:9101/metrics 2> /dev/null | grep "aptos_connections{direction=\"outbound\""
```

该命令将输出节点的出站网络连接数。 例如：

```
aptos_connections{direction="outbound",network_id="Public",peer_id="aabd651f",role_type="full_node"} 3
```

如果返回的出站连接数（例如 3）不超过 0，则意味着您的节点无法连接到 Aptos 区块链。 如果您遇到这种情况，请按照以下步骤操作，看看它们是否能解决问题：

1. 按照以下步骤将您的节点更新到最新版本：[更新步骤](#update-fullnode-with-new-release).
2. 删除您可能已添加到配置文件（例如，public\_full\_node.yaml）中的任何种子节点。 种子可能会阻止您连接到网络。种子对等点在[添加上游种子对等点](#tian-jia-shang-you-zhong-zi-jie-dian)部分中讨论。

### （可选）检查 Docker 分类帐大小[​](https://aptos.dev/tutorials/run-a-fullnode#optional-examine-docker-ledger-size) <a href="#optional-examine-docker-ledger-size" id="optional-examine-docker-ledger-size"></a>

可以通过进入 Docker 容器并检查大小来监控 DevNet 的区块链账本的体积。 这将允许您查看区块链分类帐当前消耗了多少存储空间。 运行这些命令来检查：

```
# 获得容器id:
id=$(docker container ls | grep public_full_node_fullnode_1 | grep -oE "^[0-9a-zA-Z]+")
# 进入容器：
docker exec -it $id /bin/bash
# 查看卷(ledger)大小:
du -cs -BM /opt/aptos/data
```

## **添加上游种子节点**[**​**](https://aptos.dev/tutorials/run-a-fullnode#add-upstream-seed-peers)

注意：您可能会在节点的错误消息中看到  `NoAvailablePeers` 。 这在节点首次启动时是正常的。 等待节点运行几分钟，看看它是否连接到对等点。 如果没有，请按照以下步骤操作：

Devnet 验证器全节点最多只能接受约 5000 个连接。 如果我们的网络容量很大，您的全节点可能无法连接。 您可能会在节点的错误消息中不断看到  `NoAvailablePeers` 。 如果发生这种情况，您可以在 FullNode 配置文件中设置种子以添加要连接的新对等点。 我们准备了一些 FullNode 地址供您使用，如下所示。

此外，请随意使用社区提供的那些（任何已经运行全节点的人都可以提供他们的地址供您连接）。 将这些添加到您的 `discovery_method` 下的配置文件中：

```
...
full_node_networks:
    - discovery_method: "onchain"
      # The network must have a listen address to specify protocols. This runs it locally to
      # prevent remote, incoming connections.
      listen_address: ...
      seeds:
        bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
            addresses:
            - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/ln-handshake/0"
            role: "Upstream"
        7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61:
            addresses:
            - "/dns4/pfn1.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61/ln-handshake/0"
            role: "Upstream"
        f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b:
            addresses:
            - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/ln-handshake/0"
            role: "Upstream"
...
```

## 高级指南[​](https://aptos.dev/tutorials/run-a-fullnode#advanced-guide) <a href="#advanced-guide" id="advanced-guide"></a>

如果您想深入了解节点配置的更多自定义，本高级指南将向您展示：&#x20;

* 为您的新全节点创建静态网络身份
* 检索其他节点许可列表的公共网络身份
* 启动带有或不带有静态网络标识的节点

### 为全节点创建静态身份[​](https://aptos.dev/tutorials/run-a-fullnode#create-a-static-identity-for-a-fullnode) <a href="#create-a-static-identity-for-a-fullnode" id="create-a-static-identity-for-a-fullnode"></a>

全节点将自动以随机生成的网络身份（PeerId 和公钥对）启动。 这对于常规的全节点非常有用，但如果您需要另一个节点将您列入白名单或提供特定权限，或者如果您希望始终以相同的身份运行您的全节点，则可以创建静态网络身份。

1. 使用 [aptos-labs/aptos-core](https://github.com/aptos-labs/aptos-core.git) 项目构建 `aptos-operational-tool` ，我们可以使用 cargo 构建来运行这些工具。 例如：

   ```
   $ git clone https://github.com/aptos-labs/aptos-core.git
   $ cd aptos-core
   $ ./scripts/dev_setup.sh
   $ source ~/.cargo/env
   $ cargo run -p aptos-operational-tool -- <command> <args>
   ```

   或者，您可以使用我们的 docker 镜像。 使用最新的工具版本启动一个 docker 容器，例如：

   ```
   $ docker run -i aptoslab/tools:devnet sh -x
   $ aptos-operational-tool <command> <arg>
   ```
2. 运行密钥生成器，输出十六进制编码的静态 x25519 PrivateKey。 这将是您网络身份的私钥。

   ```
    $ cargo run -p aptos-operational-tool -- generate-key --encoding hex --key-type x25519 --key-file /path/to/private-key.txt
   ```

   或者在 aptoslab/tools docker 容器内执行：

   ```
   $ aptos-operational-tool generate-key --encoding hex --key-type x25519 --key-file /path/to/private-key.txt
   ```

   key 文件示例：

   ```
   $ cat /path/to/private-key.txt
   B8BD811A91D8E6E0C6DAC991009F189337378760B55F3AD05580235325615C74
   ```

### 检索公共网络身份[​](https://aptos.dev/tutorials/run-a-fullnode#retrieve-public-network-identity) <a href="#retrieve-public-network-identity" id="retrieve-public-network-identity"></a>

1. 在前一个密钥文件上运行 peer 生成器

   ```
    $ cargo run -p aptos-operational-tool -- extract-peer-from-file --encoding hex --key-file /path/to/private-key.txt --output-file /path/to/peer-info.yaml
   ```

   或者在 aptoslab/tools docker 容器内执行：

   ```
   $ aptos-operational-tool extract-peer-from-file --encoding hex --key-file /path/to/private-key.txt --output-file /path/to/peer-info.yaml
   ```

   yaml 输出示例：

   ```
    ---
    ca3579457555c80fc7bb39964eb298c414fd60f81a2f8eedb0244ec07a26e575:
      addresses: []
      keys:
        - ca3579457555c80fc7bb39964eb298c414fd60f81a2f8eedb0244ec07a26e575
      role: Downstream
   ```

   在这个示例中， `ca3579457555c80fc7bb39964eb298c414fd60f81a2f8eedb0244ec07a26e575` 是 peer id， 也是从您在上一步生成的私钥派生的公钥。
2. 这将创建一个 yaml 文件，其中包含您的公共身份，以提供给上游完整节点。 如果您想通过特定的上游完整节点连接您的完整节点，并且该完整节点仅允许已知身份连接到它们，这将非常有用。

### 使用静态网络身份启动节点[​](https://aptos.dev/tutorials/run-a-fullnode#start-a-node-with-the-static-network-identity) <a href="#start-a-node-with-the-static-network-identity" id="start-a-node-with-the-static-network-identity"></a>

一旦我们有了静态身份，我们可以用它启动一个节点。

```
full_node_networks:
- network_id: "public"
  discovery_method: "onchain"
  identity:
    type: "from_config"
    key: "<PRIVATE_KEY>"
    peer_id: "<PEER_ID>"
```

示例：

```
full_node_networks:
- network_id: "public"
  discovery_method: "onchain"
  identity:
    type: "from_config"
    key: "B8BD811A91D8E6E0C6DAC991009F189337378760B55F3AD05580235325615C74"
    peer_id: "ca3579457555c80fc7bb39964eb298c414fd60f81a2f8eedb0244ec07a26e575"
```

### 允许其他全节点连接[​](https://aptos.dev/tutorials/run-a-fullnode#allow-other-fullnode-to-connect) <a href="#allow-other-fullnode-to-connect" id="allow-other-fullnode-to-connect"></a>

使用静态身份启动全节点后，您可以打开节点以允许其他人通过您的节点连接到 devnet。 确保在防火墙上打开端口 6180（或 6182，取决于您的节点正在侦听的端口，可以在 fullnode config yaml 文件中配置），并共享您的节点信息以供其他人用作 `seeds`。

```
<Peer_ID>:
  addresses:
  # with DNS
  - "/dns4/<DNS_Name>/tcp/<Port_Number>/ln-noise-ik/<Public_Key>/ln-handshake/0"
  role: Upstream
<Peer_ID>:
  addresses:
  # with IP
  - "/ip4/<IP_Address>/tcp/<Port_Number>/ln-noise-ik/<Public_Key>/ln-handshake/0"
  role: Upstream
```

确保您在地址中输入的端口号与您在全节点配置中的端口号（6180 或 6182）相匹配。 例子：

```
bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
  addresses:
  - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/ln-handshake/0"
  role: "Upstream"
bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
  addresses:
  - "/ip4/100.20.221.187/tcp/6182/ln-noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/ln-handshake/0"
  role: "Upstream"
```

## 更新全节点新版本 <a href="#update-fullnode-with-new-release" id="update-fullnode-with-new-release"></a>

{% hint style="info" %}
✨ DEVNET 新地址格式 ✨

Aptos 地址现在是 32 字节而不是 16 字节。 如果您之前添加了种子对等点，请确保将它们更新为新的 32 字节格式。 对于使用静态身份的人，您可以按照相同的说明[重新生成您的身份](#create-a-static-identity-for-a-fullnode)，为上面的 FullNode 创建一个静态身份。
{% endhint %}

[​](https://aptos.dev/tutorials/run-a-fullnode/#update-fullnode-with-new-release)当 devnet 被擦除并使用更新版本发布时，您还需要更新 fullnode，以确保它可以与新网络保持同步。 一般步骤如下：

1. 关闭您的全节点
2. 删除数据文件（目录路径是您在 `public_full_node.yaml` 文件中指定的）， 默认是 `/opt/aptos/data`
3. 删除 `genesis.blob` 和 `waypoint.txt` 文件（取决于您怎么配置，你也可能没有这个文件）
4. 获取最新的 [genesis](https://devnet.aptoslabs.com/genesis.blob) 和 [waypoint](https://devnet.aptoslabs.com/waypoint.txt) 文件
5. 使用新的 waypoint 更新 `public_full_node.yaml` （如果你直接将 waypoint 配置在这里）
6. 重启全节点
7. 请参阅上面的 [验证初始同步](#yan-zheng-chu-shi-hua-tong-bu) 部分以检查节点是否再次进行状态同步。&#x20;


# 运行全节点（社区教程）

开发网运行全节点教程（非测试网）


# Windows

开发网 Windows 运行全节点教程（非测试网）

## 1. 安装 Docker

在运行节点之前，我们需要安装 Docker。 您可以从[这里](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe)下载适用于 Windows 的 Docker Desktop。

下载完成后，您将在下载文件夹中找到可执行文件。

![Downloaded Docker](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FdQbmrtTb9mT7l0wSMsxv%2Fdownloaded.png?alt=media\&token=7c7ce87c-eaa6-4372-8d8b-c78de7daebff)

打开 `Docker Desktop Installer.exe`，并开始安装过程。

安装完成后，Docker 会自动启动。

![Docker Installed](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F0AqRQ8NPgbI01alG7ltw%2Finstalled.png?alt=media\&token=2460f9d0-7f7d-4c6a-8c22-8ebb14095c58)

## 2. 安装 WSL 2

Docker 需要安装额外的依赖项才能在 Windows 上运行 Linux 容器。

您将收到类似于以下通知的通知，单击提供的链接，将打开一个新的浏览器窗口，我们可以下载依赖项。

![WSL Notice](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F04sxq5aZnTrnukqvkIde%2Fwsl-notice.png?alt=media\&token=2bfe7c87-c763-4ff4-b79e-07c0b9a630fe)

点击链接 "Download the latest package"

![Download WSL 2](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FFjjDSq6KZvKG31WrHmox%2Fwsl-download.png?alt=media\&token=8c716aae-ae54-451a-8930-35629d9aadf6)

你可以在下载目录中找到可执行文件：

![WSL 2 Downloaded](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FxOFLwwPvsX1grMxKiFBH%2Fwsl-downloaded.png?alt=media\&token=7795ad94-61ed-4d69-ad28-2dd55bdbe26c)

点击可执行文件开始安装：

![Install WSL 2](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FoZip2lsWVHpuiae74ukq%2Fwsl-install.png?alt=media\&token=55ff97df-d1f4-40bb-8527-89c5c0b7f0b8)

安装完成后你可以点击重启或 Restart。&#x20;

![Restart WSL 2](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FPUcP9tCENzUj1CeHO7y2%2Fwsl-restart.png?alt=media\&token=14d6b8cc-ad1c-4b76-a063-da002fa30b22)

## 3. 启动 Docker Desktop

打开双击 Docker Desktop application you should see t应用程序，以下是启动中的截图：

![Docker Desktop App is starting](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FqGcqcZK3dhXBdazzDSWY%2Fdocker-starting-2.png?alt=media\&token=a8818be8-c152-423d-be95-eba257280a19)

当 Docker Desktop 启动成功后，黄色图标会变绿。

![Docker Desktop Started](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FXo2kgBxdtQqgC5c5rReg%2Fdocker-started.png?alt=media\&token=7a128151-7a64-49b8-8f8d-b27db65c87d3)

## 4. 下载 Aptos 节点运行所需文件

### 4.1 新建 aptos-node 文件夹

您可以在 C 盘 或 D 盘下新建 aptos-node 文件夹。

{% hint style="info" %}
注意：

* 文件夹名称不要出现中文等特殊字符。
  {% endhint %}

### 4.2 下载节点文件

请将文件都下载到 aptos-node 目录下：&#x20;

* Docker 编排脚本：[docker-compose.yaml](https://raw.githubusercontent.com/jiangydev/aptos-node/main/docker-compose.yaml) （文件如下）

{% file src="/files/l1XljlYfFJ6rgPV0k3lN" %}

* 全节点配置文件：[public full node.yaml](https://raw.githubusercontent.com/jiangydev/aptos-node/main/public_full_node.yaml)（文件如下）

{% file src="/files/G3Vo0s3miQNgwQHucz9U" %}

* 创世区块文件：[genesis.blob](https://devnet.aptoslabs.com/genesis.blob) （文件可能会经常更新，所以请点击左侧链接下载最新）
* waypoint（可验证检查点）文件：[waypoint.txt](https://devnet.aptoslabs.com/waypoint.txt)（文件可能会经常更新，所以请点击左侧链接下载最新；如果没有自动下载，请在页面上 Ctrl + s 保存即会下载）

下载完成后：

![Download Aptos node files](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fpv1EueUa1udaktntHJZN%2Fdownload-aptos-node-file.png?alt=media\&token=f96d56bf-5c2b-40c9-bac8-45d39eafe266)

## 5. 创建静态身份

创建静态身份的目的是，每次启动都使用这个身份。

在文件夹内，地址栏中输入【powershell】，即可打开命令行界面：

![Open Powershell CLI](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FOkGdkpAz9ibibJJQIzEz%2Fimage.png?alt=media\&token=89084955-f061-4d65-a4cc-8cbbe8d3954b)

### 5.1 生成私钥、公钥和 Peer ID

在打开的命令行界面分别输入下面的命令生成私钥：

```
docker pull aptoslabs/tools:devnet
```

```powershell
sudo docker run --rm aptoslabs/tools:devnet sh -c "echo '开始生成私钥...' && aptos key generate --encoding hex --key-type x25519 --output-file /root/private-key.txt && echo '\n\n开始生成公钥和 Peer ID...' && aptos key extract-peer --encoding hex --private-key-file /root/private-key.txt --output-file /root/peer-info.yaml && echo '\n\n您的私钥：' && cat /root/private-key.txt && echo '\n\n您的公钥和 Peer ID 信息如下：' && cat /root/peer-info.yaml"
```

运行的结果如下：

```
开始生成私钥...
{
  "Result": "Success"
}


开始生成公钥和 Peer ID...
{
  "Result": {
    "3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764": {
      "addresses": [],
      "keys": [
        "3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764"
      ],
      "role": "Downstream"
    }
  }
}


您的私钥：
08500EDFBE94721A8D5614529AAC4E1AF427BB47498EC0E1A65815057137516A

您的公钥和 Peer ID 信息如下：
---
3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764:
  addresses: []
  keys:
    - 3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764
  role: Downstream
```

* `08500EDFBE94721A8D5614529AAC4E1AF427BB47498EC0E1A65815057137516A` 就是私钥；
* `3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764` 是 Peer ID，也是公钥；

{% hint style="info" %}
⚠ 上面的私钥、公钥（Peer ID）都需要备份！
{% endhint %}

### 5.2 修改全节点的静态身份

打开 `public_full`\_`node.yaml` 文件【文件名上右击 -> 打开方式 -> 记事本】，并修改成如下信息：

{% hint style="info" %}
提醒：

* 请使用上一步自己生成的私钥和 Peer ID，以下示例仅供参考。
* 请妥善保存好私钥；（公钥和 Peer ID 是由私钥生成的）
* 一定要按如下格式修改。（下面有替换示例截图🙂）
  {% endhint %}

```yaml
base:
    # This is the location Aptos will store its database. It is backed by a dedicated docker volume
    # for persistence.
    data_dir: "/opt/aptos/data"
    role: "full_node"
    waypoint:
        # This is a checkpoint into the blockchain for added security.
        from_file: "/opt/aptos/etc/waypoint.txt"

execution:
    # Path to a genesis transaction. Note, this must be paired with a waypoint. If you update your
    # waypoint without a corresponding genesis, the file location should be an empty path.
    genesis_file_location: "/opt/aptos/etc/genesis.blob"

state_sync:
  state_sync_driver:
    enable_state_sync_v2: true

full_node_networks:
    - network_id: "public"
      discovery_method: "onchain"
      identity:
        type: "from_config"
        key: "这里换成自己生成的私钥"
        peer_id: "这里换成自己生成的PeerID"
      # The network must have a listen address to specify protocols. This runs it locally to
      # prevent remote, incoming connections.
      listen_address: "/ip4/0.0.0.0/tcp/6180"
      # Define the upstream peers to connect to
      seeds:
        bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
            addresses:
            - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/handshake/0"
            role: "Upstream"
        7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61:
            addresses:
            - "/dns4/pfn1.node.devnet.aptoslabs.com/tcp/6182/noise-ik/7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61/handshake/0"
            role: "Upstream"
        f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b:
            addresses:
            - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/handshake/0"
            role: "Upstream"

api:
    # This specifies your REST API endpoint. Intentionally on public so that Docker can export it.
    address: 0.0.0.0:8080

```

替换示例：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FYpaFuQNTpU20FhjS560w%2Fimage.png?alt=media\&token=648ac949-8feb-4831-8902-6fa05e5001a0)

## 6. 运行全节点

在前面的文件夹中，地址栏中输入【powershell】，打开命令行界面：

![Open Powershell CLI](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FOkGdkpAz9ibibJJQIzEz%2Fimage.png?alt=media\&token=89084955-f061-4d65-a4cc-8cbbe8d3954b)

在命令中分别输入如下命令：

```
docker-compose pull
```

```
docker-compose up -d
```

![Run aptos node](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FxxVnBy0slJYiuexhddFe%2Frun-aptos-node.png?alt=media\&token=3ce238ce-38a0-4fc8-bf26-0d94c56a8529)

左侧是运行的结果，右侧是在 Docker Desktop 中看到运行中状态的节点。你可以在图形化界面中管理你的节点状态。

## 7. 检查节点

在浏览器中访问：<http://127.0.0.1:9101/metrics>

{% hint style="info" %}
确保节点是运行中，否则无法访问上面的地址！
{% endhint %}

### 7.1 查看 Peer ID

在页面搜索：peer

* Peer ID 要是上面自己填写的前缀；

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FJmA7he7djfeHG19HujSn%2Fnode-check-peer-id.png?alt=media\&token=9096090c-432c-4214-9c2a-35a310ae9f8a)

### 7.2 查看同步状态

在页面搜索：aptos\_state\_sync\_version

Aptos 网络看板可以查看当前区块高度（用于检查本地节点同步情况）：[https://status.devnet.aptos.dev](https://status.devnet.aptos.dev/)

## 8. 节点更新

{% hint style="info" %}
本节仅用于每周五的节点更新使用。
{% endhint %}

### 8.1 关闭节点

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FC0uJ7czcRjMj9MvnC8uE%2Fimage.png?alt=media\&token=debf57b9-d79d-4a26-924a-59606bc9bf58)

### 8.2 删除数据文件

在【Volumes】中找到 node\_db 并点击删除。

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FbgalRq7UcpJ83NPEx6At%2Fimage.png?alt=media\&token=a1aae7b2-a075-40e3-a798-741e3d9039b5)

### 8.3 删除 `genesis.blob` 和 `waypoint.txt` 文件并重新下载

进入之前下载文件的目录，重新下载覆盖以下两个文件：

* 创世区块文件：[genesis.blob](https://devnet.aptoslabs.com/genesis.blob)&#x20;
* waypoint（可验证检查点）文件：[waypoint.txt](https://devnet.aptoslabs.com/waypoint.txt)

{% hint style="info" %}
注意：

* 2022年6月10日更新：<mark style="color:red;">本周必须更新</mark><mark style="color:red;">`public_full`</mark><mark style="color:red;">\_</mark><mark style="color:red;">`node.yaml`</mark> <mark style="color:red;"></mark><mark style="color:red;">文件</mark>。
  {% endhint %}

### 8.4 更新节点镜像

在更新文件的目录下进入命令行界面，并运行如下命令：

```
docker-compose pull
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FXazjoAu96y8WTgma3ljo%2Fimage.png?alt=media\&token=83f94e61-472f-4cab-b9dc-781b8dc57704)

### 8.5 启动全节点

在上面的命令行界面中运行如下命令：

```
docker-compose up -d
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FuDKs7At5AHrZVUTIEN25%2Fimage.png?alt=media\&token=06367b96-7572-45fb-a31e-307bc9af2f90)


# Linux

开发网 Linux 运行全节点教程（非测试网）

服务器最低要求：2C 8G （注：2C 4G 也可以）

使用的系统：Ubuntu 20.04

## 1. 安装 Docker 和 docker-compose

{% hint style="info" %}
如果你已经安装过，可以跳过本步骤。
{% endhint %}

依次执行如下命令安装 Docker 和 docker-compose。

```shell
# 安装 Docker
wget -O get-docker.sh https://get.docker.com 
sudo sh get-docker.sh
rm -f get-docker.sh

# 安装 docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
```

检查 Docker 和 docker-compose 是否安装：

```
docker -v
docker-compose -v
```

![Check docker and docker-compose](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FyQVGPECwzX22i13KpIYj%2Flinux-docker-installed.png?alt=media\&token=551cae01-c399-4439-beb7-e2e91e6a5511)

## 2. 下载 Aptos 节点运行所需文件

依次执行如下命令：

```shell
# 创建 aptos-node 目录并进入该目录，之后的操作都会在该目录下进行
mkdir -p ~/aptos-node && cd ~/aptos-node
# 下载 docker-compose 编排文件
wget -O ./docker-compose.yaml https://raw.githubusercontent.com/jiangydev/aptos-node/main/docker-compose.yaml
# 下载 全节点配置文件
wget -O ./public_full_node.yaml https://raw.githubusercontent.com/jiangydev/aptos-node/main/public_full_node.yaml
# 下载 创世节点文件
wget -O ./genesis.blob https://devnet.aptoslabs.com/genesis.blob
# 下载 waypoint（可验证检查点）文件
wget -O ./waypoint.txt https://devnet.aptoslabs.com/waypoint.txt
```

下载完成后，使用命令 `ls -al` 查看目录下的文件：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F9idKZeCWwINTZ4JbBX7R%2Flinux-downloaded.png?alt=media\&token=e4e69e0d-dd6e-4f6b-9e3b-4e687fc5f4f6)

## 3. 创建静态身份

创建静态身份的目的是，每次启动都使用这个身份。

### 3.1 生成私钥、公钥和 Peer ID

在之前的目录下，执行命令生成私钥：

```powershell
# 更新镜像
sudo docker pull aptoslabs/tools:devnet
# 生成
sudo docker run --rm aptoslabs/tools:devnet sh -c "echo '开始生成私钥...' && aptos key generate --encoding hex --key-type x25519 --output-file /root/private-key.txt && echo '\n\n开始生成公钥和 Peer ID...' && aptos key extract-peer --encoding hex --private-key-file /root/private-key.txt --output-file /root/peer-info.yaml && echo '\n\n您的私钥：' && cat /root/private-key.txt && echo '\n\n您的公钥和 Peer ID 信息如下：' && cat /root/peer-info.yaml"

```

运行的结果如下：

```
开始生成私钥...
{
  "Result": "Success"
}


开始生成公钥和 Peer ID...
{
  "Result": {
    "92c826d2733c391a4f8ee6d4e573b764": {
      "addresses": [],
      "keys": [
        "3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764"
      ],
      "role": "Downstream"
    }
  }
}


您的私钥：


您的公钥和 Peer ID 信息如下：
---
3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764:
  addresses: []
  keys:
    - 3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764
  role: Downstream
```

* `08500EDFBE94721A8D5614529AAC4E1AF427BB47498EC0E1A65815057137516A` 就是私钥；
* `3129d20a6ebb3a92be794db60416e6eb92c826d2733c391a4f8ee6d4e573b764` 是 Peer ID，也是公钥；

### 3.2 修改全节点的静态身份

编辑 public full node.yaml 文件，并修改私钥和 Peer ID：

{% hint style="info" %}
提醒：

* 请使用上一步自己生成的私钥和 Peer ID，以下示例仅供参考。
* 请妥善保存好私钥；（公钥和 Peer ID 是由私钥生成的）
* 请一定要按如下格式修改。
  {% endhint %}

{% hint style="info" %}
如何编辑文件？

1. 在命令行界面使用 vi 编辑打开文件，命令：`vi public`\_`full_node.yaml`
2. 进入后，键盘上按下英文字母 `i`，即可开始编辑；（注：输入法切换到英文半角）
3. 编辑完成后，键盘上按下 esc 键，退出编辑；
4. 输入 `:wq!`，保存并退出。
   {% endhint %}

```yaml
base:
    # This is the location Aptos will store its database. It is backed by a dedicated docker volume
    # for persistence.
    data_dir: "/opt/aptos/data"
    role: "full_node"
    waypoint:
        # This is a checkpoint into the blockchain for added security.
        from_file: "/opt/aptos/etc/waypoint.txt"

execution:
    # Path to a genesis transaction. Note, this must be paired with a waypoint. If you update your
    # waypoint without a corresponding genesis, the file location should be an empty path.
    genesis_file_location: "/opt/aptos/etc/genesis.blob"

state_sync:
  state_sync_driver:
    enable_state_sync_v2: true

full_node_networks:
    - network_id: "public"
      discovery_method: "onchain"
      identity:
        type: "from_config"
        key: "这里换成自己生成的私钥"
        peer_id: "这里换成自己生成的PeerID"
      # The network must have a listen address to specify protocols. This runs it locally to
      # prevent remote, incoming connections.
      listen_address: "/ip4/0.0.0.0/tcp/6180"
      # Define the upstream peers to connect to
      seeds:
        bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
            addresses:
            - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/handshake/0"
            role: "Upstream"
        7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61:
            addresses:
            - "/dns4/pfn1.node.devnet.aptoslabs.com/tcp/6182/noise-ik/7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61/handshake/0"
            role: "Upstream"
        f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b:
            addresses:
            - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/handshake/0"
            role: "Upstream"

api:
    # This specifies your REST API endpoint. Intentionally on public so that Docker can export it.
    address: 0.0.0.0:8080

```

修改后的结果如下：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F8Kg1Q1Cxl4PQCnB7gRYZ%2Fimage.png?alt=media\&token=9fd1e526-c7af-4858-902c-fb687ab9e23c)

## 4. 运行全节点

在前面的目录下，输入如下命令运行节点：

```
# 更新镜像
sudo docker-compose pull

# 运行节点
sudo docker-compose up -d
```

查看节点运行状态：

```
sudo docker ps -a 
```

![node container running](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FcvDy3C1Hl5hXusgVjTeC%2Flinux-node-pod-running.png?alt=media\&token=5e2942e9-9dad-4f4e-ac70-4126aedd9d91)

## 5. 检查节点

{% hint style="info" %}
确保节点是运行中，否则执行下面的命令会出错！
{% endhint %}

### 5.1 查看 Peer ID

```
curl 127.0.0.1:9101/metrics 2> /dev/null | grep -m 1 peer_id
```

### 5.2 查看同步状态

```
curl 127.0.0.1:9101/metrics 2> /dev/null | grep aptos_state_sync_version
```

Aptos 网络看板：[https://status.devnet.aptos.dev](https://status.devnet.aptos.dev/)

社区提供的检查节点状态网址：

* [https://aptos-node.info](https://aptos-node.info/)

## 6. 节点更新

{% hint style="info" %}
本节仅用于每周五的节点更新使用。
{% endhint %}

### 6.1 关闭节点

执行如下命令：

```shell
# 进入之前创建的 aptos-node 目录，之后的操作都会在该目录下进行
cd ~/aptos-node
# 关闭节点
sudo docker-compose down
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FYMuAigrAMQcy2X2nM9pG%2Fimage.png?alt=media\&token=0bc2d3bc-8031-43fb-aadb-7663a234e665)

### 6.2 删除数据文件

执行如下命令：

```shell
# 查看数据卷
sudo docker volume ls

# 删除数据卷（以上一条命令查询到的 Volume Name 结果为准）
sudo docker volume rm aptos-node_db
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fn9dpSCeBVkaxgmqZHhfz%2Fimage.png?alt=media\&token=8fb98fa4-30dd-4f65-a307-8c3d0c95398a)

### 6.3 删除 `genesis.blob` 和 `waypoint.txt` 文件并重新下载

执行如下命令即可下载并覆盖原文件：

```shell
# 下载 创世节点文件
wget -O ./genesis.blob https://devnet.aptoslabs.com/genesis.blob
# 下载 waypoint（可验证检查点）文件
wget -O ./waypoint.txt https://devnet.aptoslabs.com/waypoint.txt
```

{% hint style="info" %}
注意：

* 2022年6月10日更新：<mark style="color:red;">本周必须更新</mark><mark style="color:red;">`public_full`</mark><mark style="color:red;">\_</mark><mark style="color:red;">`node.yaml`</mark> <mark style="color:red;"></mark><mark style="color:red;">文件</mark>。
  {% endhint %}

### 6.4 更新节点镜像

运行如下命令：

```shell
sudo docker-compose pull
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FwYpv2vSkU1c41BMDK97q%2Fimage.png?alt=media\&token=71394bd5-b684-4832-a0ab-23eaae232c07)

### 6.5 启动全节点

在上面的命令行界面中运行如下命令：

```shell
sudo docker-compose up -d
```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FoxpgBdPuaUV0qYmsX2Lv%2Fimage.png?alt=media\&token=ac81191e-678c-4567-b21e-5b2b88337885)


# 测试网 Testnet


# 社区教程


# AIT-3 新功能

AIT-3 参与者正在测试几个新功能。见下文：

#### Petra（Aptos 钱包） <a href="#petra-aptos-wallet" id="petra-aptos-wallet"></a>

* 新的 Aptos 钱包 Petra 现在可作为 Chrome 网络应用程序扩展使用。您将使用此钱包参与 AIT-3 的质押和治理。请在此处[查看 Petra 的安装说明](/jiao-cheng/petra-qian-bao-cha-jian-an-zhuang-shi-yong)。

#### 所有者、运营商和投票者角色 <a href="#owner-operator-and-voter-personas" id="owner-operator-and-voter-personas"></a>

现在可以通过三个角色参与质押和治理：所有者、运营商和投票者。

* **所有者**：所有者是资金的所有者。例如，所有者 Bob 可以将他的操作地址分配给受信任的验证器操作员 Alice 的帐户。另请参阅[托管人如何在 Aptos 上质押](https://aptos.dev/concepts/staking#how-a-custodian-can-stake-on-aptos)。所有者帐户包含验证器设置和硬币。代币被空投到所有者帐户中。
* **运营商**：如果您是所有者，则使用您的所有者密钥，您将选择特定的操作员，您将：
  * 管理特定验证器的设置
  * 将权益池委托给验证者。
  * 运营商公钥与您（所有者）在 Aptos 社区平台注册验证者节点时使用的密钥相同。可以查看验证器的“aptosbot.yaml”文件中的 `account_public_key`。
* **投票者**：投票者是 AIT-3 中引入的新角色。所有者可以指定投票者。这使投票者能够参与治理。您将使用投票人密钥签署交易中的治理投票。

#### 质押 <a href="#staking" id="staking"></a>

* 新的质押用户界面，更容易管理质押。

#### 链上治理 <a href="#on-chain-governance" id="on-chain-governance"></a>

社区对提案进行投票。提案将用于以下范围：

* 区块链链上配置，例如 Epoch 长度。
* 质押配置，例如奖励率。
* 治理配置，例如投票权要求。
* Move 模块升级。


# 节点要求

硬件、存储、网络带宽和端口要求

遵循本文档中指定的要求，使您无忧部署 AIT-3 验证器和全节点。

### 验证器和全节点 <a href="#validator-and-fullnode" id="validator-and-fullnode"></a>

* 对于 AIT-3，我们要求您运行 Validator。我们建议您也可以选择运行 FullNode。但是，FullNode 不是必需的。
* 如果您还运行 FullNode，那么我们强烈建议您在两台独立的机器上运行 Validator 和 FullNode。确保这些机器配置良好并且彼此隔离。保证 Validator 和 FullNode 之间的资源隔离将有助于确保这些节点的顺利部署。
* 为了获得最佳可用性和稳定性，**我们建议您将节点部署在云上**。我们为在三个云提供商上部署节点提供了 Terraform 支持：GCP、AWS 和 Azure。
* 确保在 AIT 上线之前打开网络端口。
* 确保在被 AIT 接受或拒绝后关闭这些端口。

### 测试模式下 <a href="#validator-node-in-test-mode" id="validator-node-in-test-mode"></a>

您必须在测试模式下运行 Validator 节点才有资格使用 AIT-3。这是 Aptos Labs 用来验证节点操作员是否可以成功启动 Validator 节点并使用 Aptos 网络身份正确配置它的方法。

在测试模式下，您将运行一个具有单个节点的本地网络，它应该像普通的区块链一样运行。

### 硬件要求 <a href="#hardware-requirements" id="hardware-requirements"></a>

为了运行 Aptos **Validator 和 Fullnode**，我们推荐以下硬件资源：

* **CPU**
  * <mark style="color:red;">8核16线程</mark>
  * 2.8GHz 或更快
  * 英特尔至强 Skylake 或更新版本
* **内存**：<mark style="color:red;">32GB RAM</mark>。

各种云上的示例机器类型：

* AWS（亚马逊云）
  * c5.4xlarge
  * c6i.4xlarge
* GCP（谷歌云）
  * c2-standard-16
* Azure（微软云）
  * Standard\_B8ms

### 存储要求 <a href="#storage-requirements" id="storage-requirements"></a>

Aptos Blockhain 存储的数据量取决于区块链的账本历史（长度）和链上状态（例如账户）的数量。这些值取决于几个因素，包括：区块链的年龄、平均交易率和账本修剪器的配置。

我们建议节点<mark style="color:red;">至少有 300GB</mark> 的磁盘空间，以确保有足够的存储空间进行负载测试。您可以选择从较小的尺寸开始并根据需要进行调整。您将负责监控节点的磁盘使用情况并进行适当调整以确保节点正常运行。

### 网络要求 <a href="#networking-requirements" id="networking-requirements"></a>

带宽要求：1 Gbps

#### 端口 <a href="#ports" id="ports"></a>

当您运行验证器节点时，您需要在您的节点上打开网络端口以允许其他节点连接到您。对于全节点，这是可选的。

Aptos 网络共有三种类型。可以配置您的节点，以便这些网络中的每一个都可以使用节点上的不同端口连接到您的节点。

1. 验证者网络：验证者节点连接到该网络。
2. 公共网络：公共全节点连接到该网络。
3. 验证者全节点网络（VFN 网络）：验证者全节点连接到该网络。VFN 网络允许验证者全节点连接到特定的验证者。

您可以使用配置 YAML 文件在节点上配置端口设置。请参阅[此处的示例配置 YAML](https://github.com/aptos-labs/aptos-core/blob/4ce85456853c7b19b0a751fb645abd2971cc4c0c/docker/compose/aptos-node/fullnode.yaml#L10-L9)。在您的节点上使用此配置 YAML，公共网络在端口 6182 上连接到您的节点，在 6181 上连接到 VFN 网络。因为这些端口设置是可配置的，所以我们没有明确说端口 X 用于网络 Y。

#### 端口设置 <a href="#port-settings" id="port-settings"></a>

对于验证器：

* 打开 TCP 端口 6180，以便验证器相互通信。
* 打开 TCP 端口 9101，以获取 Validator 指标以验证运行状况统计信息（仅在注册阶段需要）。

对于全节点：

* 打开 TCP 端口 6182，以便全节点相互通信。
* 打开 TCP 端口 9101，获取 Fullnode 指标以验证运行状况统计信息（仅在注册阶段需要）。
* 打开 TCP 端口 80/8080，用于 REST API 访问。


# AIT-3 步骤

请参阅下面的摘要流程图和您在参与 AIT-3 时将执行的详细步骤。

{% hint style="danger" %}
仅限 CHROME 浏览器

仅 Chrome 浏览器支持新的 Petra (Aptos Wallet)。因此，对于以下所有 AIT-3 任务，请确保您仅使用 Chrome 浏览器。
{% endhint %}

### 一、登录、连接钱包并完成调查 <a href="#sign-in-connect-wallet-and-complete-survey" id="sign-in-connect-wallet-and-complete-survey"></a>

AIT-3 计划的参与者必须展示配置和部署节点的能力，并通过[节点健康检查器](https://aptos.dev/nodes/node-health-checker)报告的最低性能要求。

#### 总结步骤 <a href="#summary-steps" id="summary-steps"></a>

1. 注册或登录 Aptos 社区 AIT-3 网站：<https://aptoslabs.com/it3>
2. 安装 Petra 钱包插件，[点击查看教程](/jiao-cheng/petra-qian-bao-cha-jian-an-zhuang-shi-yong)
3. 创建第一个钱包
4. 连接钱包
5. 完成调研
6. 部署节点

#### 详细步骤 <a href="#detailed-steps" id="detailed-steps"></a>

要参与 AIT-3 计划，请按照以下步骤操作。使用这些步骤作为清单来跟踪您的进度。单击每个步骤中的链接以获取详细文档。

1. 导航到[Aptos 社区页面](https://aptoslabs.com/community)并按照步骤操作，首先注册或登录您的 Discord 帐户。
2. 在您点击第 2 步**连接钱包**之前：
   1. 删除您在 Chrome 上安装的任何以前版本的 Aptos Wallet
   2. 使用第 3 步说明安装 Petra (Aptos Wallet) 扩展，然后
   3. 使用第 4 步说明创建第一个钱包。
3. [按照此处的说明](/jiao-cheng/petra-qian-bao-cha-jian-an-zhuang-shi-yong)在您的 Chrome 浏览器上**安装**Petra (Aptos Wallet) 扩展程序。
4. 使用 Petra (Aptos Wallet) 创建第一个钱包。

   **第一个钱包将永远是所有者钱包**。

   1. 从 Chrome 浏览器的扩展部分打开 Aptos Wallet 扩展，或者单击浏览器右上角的拼图并选择 Aptos Wallet。
   2. 单击**创建新钱包**。
   3. 确保将种子短语存储在安全的地方。该帐户将在将来使用。
5. 单击第 2 步**连接钱包**，将所有者钱包地址注册到您的 Aptos 社区帐户。Aptos 团队将向该所有者钱包地址空投代币。
6. 单击步骤 3完成**调查**。
7. 接下来，继续安装和部署验证器节点。

### 二、部署验证者节点并注册节点 <a href="#deploy-the-validator-node-and-register-the-node" id="deploy-the-validator-node-and-register-the-node"></a>

#### 总结步骤 <a href="#summary-steps-1" id="summary-steps-1"></a>

![](https://aptos.dev/img/docs/install-validator-and-register.svg)

#### 详细步骤 <a href="#detailed-steps-1" id="detailed-steps-1"></a>

1. 阅读节点要求。

   小费

   在继续之前，请确保您的硬件、存储和网络资源满足[节点要求](https://aptos.dev/nodes/ait/node-requirements)。
2. 按照验证器中提供的详细节点安装步骤，[在](https://aptos.dev/nodes/validator-node/validators)测试模式下部署验证器节点。

   **确保将您的节点设置为测试模式。**&#x8282;点安装部分提供了说明。Aptos Labs 需要测试模式才能对您的节点进行健康检查。
3. 返回 Aptos 社区页面并通过单击第 4 步：**节点注册**按钮注册您的节点。

   在此节点注册屏幕上提供您的验证器节点的详细信息，您需要的所有公钥信息都在`~/$WORKSPACE/keys/public-keys.yaml`文件中（请不要输入私钥中的任何内容）。

   * OWNER KEY：第一个钱包公钥。从设置 -> 凭据
   * 共识密钥：consensus\_public\_key 来自`public-keys.yaml`
   * 共识 POP：consensus\_proof\_of\_possession 来自`public-keys.yaml`
   * 帐户密钥：account\_public\_key 来自`public-keys.yaml`
   * 验证器网络密钥：validator\_network\_public\_key 来自`public-keys.yaml`
4. 接下来，单击**VALIDATE NODE**。如果您的节点通过了健康检查，系统将提示您完成身份验证过程。

   Aptos 团队将使用[Node Health Checker](https://aptos.dev/nodes/node-health-checker)对您的验证器执行节点健康检查。当 Aptos 确认您的节点健康时，您将被要求完成 KYC 流程。
5. 等待评选公告。如果您被选中，Aptos 团队会将硬币空投到您的所有者钱包地址。如果您没有在您的所有者钱包中看到空投硬币，则您没有被选中。
6. 如果您被选中，则继续执行**Iniatilize staking pool**步骤。

### 三、初始化质押池 <a href="#initialize-staking-pool" id="initialize-staking-pool"></a>

#### 总结步骤 <a href="#summary-steps-2" id="summary-steps-2"></a>

![签名交易流程](https://aptos.dev/img/docs/initialize-staking-pool.svg)

#### 详细步骤 <a href="#detailed-steps-2" id="detailed-steps-2"></a>

在您继续之前

仅当您被选中参加 AIT-3 时，才继续执行以下步骤。

1. 通过检查您的 Petra 钱包的余额，确认您收到了 Aptos 团队的代币。**单击设置**→**网络**，确保您已连接到 AIT-3 网络。
2. 为选民创建另一个钱包地址。参见[上面的第 4 步：使用 Petra](https://aptos.dev/nodes/ait/steps-in-ait3#create-wallet)创建钱包在 Petra 上创建钱包。此步骤是可选的。您也可以将所有者钱包帐户用作选民钱包。但是，最好的做法是拥有一个专用的投票账户，这样您就不需要经常访问您的所有者密钥来进行治理操作。
3. **接下来，您将进行质押和委托。**

   阅读质押文件

   在继续之前，请确保您阅读了[Staking文档。](https://aptos.dev/concepts/staking)

   您将首先初始化质押池并委托给运营商和选民。

   1. 在 Chrome 浏览器中，转到[AIT-3 的 Aptos Governance 页面的](https://explorer.devnet.aptos.dev/proposals/stake?network=ait3)[**Staking 部分**](https://explorer.devnet.aptos.dev/proposals/stake?network=ait3)。
   2. 确保钱包与您的**所有者**帐户相关联。
   3. 提供以下输入：
      1. 质押金额：100000000000000（100 万个 Aptos 币，小数点后 8 位）
      2. 操作员地址：您的操作员帐户的地址。这是`operator_account_address`来自文件夹下的“operator.yaml”文件`~/$WORKSPACE/$USERNAME`。
      3. 选民地址：您的选民的钱包地址。
   4. 点击**提交**。您将看到一个绿色的小吃条，表示交易成功。
4. 接下来，作为所有者，使用 Petra 钱包，将 5000 个硬币转移到您的运营商地址和选民钱包地址。运营商和选民都将在验证和投票时使用这些资金支付 Gas 费。
5. 继续**连接到 AIT-3 并加入验证器集**。

### 四、连接到 AIT-3 并加入验证器集 <a href="#connect-to-ait-3-and-join-the-validator-set" id="connect-to-ait-3-and-join-the-validator-set"></a>

#### 详细步骤 <a href="#detailed-steps-3" id="detailed-steps-3"></a>

请参阅[连接到 Aptos 激励测试网](/jiao-cheng/ce-shi-wang-testnet/she-qu-jiao-cheng/jia-ru-ce-shi-wang)。

### 五、投票 <a href="#vote" id="vote"></a>

您将在此步骤中测试投票功能。

1. 在 Chrome 浏览器中，转到[AIT-3 Aptos 治理页面的](https://explorer.devnet.aptos.dev/proposals?network=ait3)[**提案部分**](https://explorer.devnet.aptos.dev/proposals?network=ait3)。
2. 查看提案。当您准备好对提案进行投票时，请单击该提案。
3. 确保您将钱包与您的**选民**钱包帐户相关联。
4. 提供您的**所有者**帐户地址并投票“赞成”或“反对”。
5. 您将看到一个绿色的提示条，表示交易成功。


# 使用 Docker

AIT-3 激励测试网教程（Incentivized Testnet Tutorial）

{% hint style="danger" %}
如果你之前运行过 devnet 全节点或测试网节点，请先停止，防止端口冲突导致无法运行测试网验证者节点。

If you have run a devnet full node before, please stop it first to prevent port conflicts from running the testnet validator node.

请收藏本网址以获得最新教程！！！
{% endhint %}

### 节点要求

* CPU: 8 核 16 线程
* RAM：32GB
* 存储：300 GB
* 带宽：1 Gbps

详细要求请参阅 [节点要求](#jie-dian-yao-qiu)

### 安装 Docker 和 docker-compose（Install Docker and docker-compose）

```shell
# Install Docker
wget -O get-docker.sh https://get.docker.com 
sudo sh get-docker.sh
rm -f get-docker.sh

# Install docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

```

### 创建目录（Create Directory）

```bash
sudo mkdir -p ~/aptos-node/testnet3 && cd ~/aptos-node/testnet3

```

### 生成密钥对（节点所有者密钥、共识密钥和网络密钥）（Generate private keys）

```bash
sudo docker run --rm \
  -v $(pwd)/keys:/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.1 \
  aptos genesis generate-keys --output-dir /data/aptos-cli

```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fn5BfH9XoKmZoLNjsk7EO%2Fimage.png?alt=media\&token=4473eb69-57d5-4d96-9016-d3cdb64d4ef6)

### 下载编排文件（Download files）

```bash
sudo wget -O docker-compose.yaml https://raw.githubusercontent.com/aptos-labs/aptos-core/main/docker/compose/aptos-node/docker-compose.yaml
sudo wget -O validator.yaml https://raw.githubusercontent.com/aptos-labs/aptos-core/main/docker/compose/aptos-node/validator.yaml

```

### 生成验证者配置（Generate Validator Info）

{% hint style="warning" %}
只需要修改以下信息：（Only need to modify these config）

* 您的节点名称\<your-node-name>
* 验证者节点IP或DNS域名:端口\<your-vlidator-node-ip:port>
* 全节点IP或DNS域名:端口\<your-full-node-ip:port>，<mark style="color:red;">如果你要搭建全节点，这里要正确填写！！！</mark>

`MoveMove` 是我的节点名称（欢迎加入 MoveMove 社区） 😀（`MoveMove` is my node name）
{% endhint %}

```bash
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.1 \
  aptos genesis set-validator-configuration \
  --local-repository-dir /data/aptos-cli \
  --owner-public-identity-file /data/aptos-cli/keys/public-keys.yaml \
  --stake-amount 100000000000000 \
  --username 您的节点名称<your-node-name> \
  --validator-host 验证者节点IP或DNS域名:端口<your-vlidator-node-ip:port> \
  --full-node-host 全节点IP或DNS域名:端口<your-full-node-ip:port>
```

示例（Example）：

```bash
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.1 \
  aptos genesis set-validator-configuration \
  --local-repository-dir /data/aptos-cli \
  --owner-public-identity-file /data/aptos-cli/keys/public-keys.yaml \
  --username MoveMove \
  --validator-host 35.232.235.205:6180 \
  --full-node-host 35.232.235.205:6182 \
  --stake-amount 100000000000000

```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FB1D3BDHHIDSnTPzMfDjA%2Fimage.png?alt=media\&token=fff7e465-380d-4e4b-8738-c354362eb0d9)

### 生成 layout 文件（Generate layout YAML file）

{% hint style="warning" %}

* needn't modify the root key（官方文档中提及，不需要修改 root\_key）
* modify the node name（只需要修改 您的节点名称）
* The following command is a statement, please modify and execute after copying all of them.（下面的命令是一条语句，请全部<mark style="color:red;">复制后修改</mark>并执行）
  {% endhint %}

```shell
sudo bash -c "cat > layout.yaml" <<EOF
---
root_key: "D04470F43AB6AEAA4EB616B72128881EEF77346F2075FFE68E14BA7DEBD8095E"
users: ["您的节点名称<your-node-name>"]
chain_id: 43
allow_new_validators: false
epoch_duration_secs: 7200
is_test: true
min_stake: 100000000000000
min_voting_threshold: 100000000000000
max_stake: 100000000000000000
recurring_lockup_duration_secs: 86400
required_proposer_stake: 100000000000000
rewards_apy_percentage: 10
voting_duration_secs: 43200
voting_power_increase_limit: 20
EOF

```

示例：（Example）

```bash
sudo bash -c "cat > layout.yaml" <<EOF
---
root_key: "D04470F43AB6AEAA4EB616B72128881EEF77346F2075FFE68E14BA7DEBD8095E"
users: ["MoveMove"]
chain_id: 43
allow_new_validators: false
epoch_duration_secs: 7200
is_test: true
min_stake: 100000000000000
min_voting_threshold: 100000000000000
max_stake: 100000000000000000
recurring_lockup_duration_secs: 86400
required_proposer_stake: 100000000000000
rewards_apy_percentage: 10
voting_duration_secs: 43200
voting_power_increase_limit: 20
EOF

```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F0L2EBTQ3UVoeW9qM2rIb%2Fimage.png?alt=media\&token=4fc11b78-92b2-4446-82f9-bb06a8bea79f)

### 生成 genesis blob 和 waypoint（Generate genesis blob  and waypoint file）

```bash
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.1 \
  sh -c "rm -rf /data/aptos-cli/genesis.blob && rm -rf /data/aptos-cli/waypoint.txt && rm -rf /data/aptos-cli/framework.mrb && cp -r /framework.mrb /data/aptos-cli && aptos genesis generate-genesis --local-repository-dir /data/aptos-cli --output-dir /data/aptos-cli && rm -rf /data/aptos-cli/framework.mrb"

```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMB70K3rEzOAVcJxAX0EY%2Fimage.png?alt=media\&token=5c753faf-454e-4731-b147-ec78d51cfc1d)

### 启动节点（Run node）

```bash
sudo docker-compose pull
sudo docker-compose up -d

```

{% hint style="warning" %}
如果您的节点端口不通，请关闭服务器防火墙，并在云服务器提供商的安全策略中放行如下端口：

验证者节点（本教程只提供验证者节点搭建）：

* 80：validator REST API 端口；
* 6180：validator 的 peer 通信端口；
* 6181：fullnode 通信端口；
* 9101：validator 指标端口；

全节点：

* 6182：full node 的 peer 信端口；
* 80：full node REST API 端口；
* 9101：full node 指标端口；
  {% endhint %}

### 申请激励测试网（Apply for Incentivized Testnet）

网站（Website）：<https://community.aptoslabs.com/>

```bash
sudo cat ~/aptos-node/testnet3/keys/public-keys.yaml
```

* OWNER KEY: 安装钱包插件后，钱包的公钥，Settings -> Credentials
* CONSENSUS KEY:  `public-keys.yaml` 文件中的 consensus\_public\_key
* CONSENSUS POP: `public-keys.yaml` 文件中的 consensus\_proof\_of\_possession
* ACCOUNT KEY: `public-keys.yaml` 文件中的 account\_public\_key
* VALIDATOR NETWORK KEY: `public-keys.yaml` 文件中的 validator\_network\_public\_key

### 全节点搭建（可选）

请参考下一篇

对教程有建议？欢迎加入 MoveMove 社区：<https://discord.gg/Vsj7Jwny9G>


# 全节点搭建

AIT-3 激励测试网，全节点搭建

{% hint style="warning" %}

1. 建议在新的服务器上运行，本教程也是以新的服务器为例。
2. 搭建全节点要使用到验证者节点的文件，请确保在搭建验证者节点时，填写了正确的全节点 IP 或域名。
   {% endhint %}

### 创建全节点目录

{% code overflow="wrap" %}

```bash
# 切换到 root 用户
sudo su

# 创建全节点目录
mkdir -p /root/aptos-node/movemove/testnet3/keys
cd /root/aptos-node/movemove/testnet3

```

{% endcode %}

### 下载全节点配置文件和编排文件

{% code overflow="wrap" lineNumbers="true" %}

```bash
sudo wget -O docker-compose-fullnode.yaml https://raw.githubusercontent.com/jiangydev/aptos-node/main/testnet/AIT3/docker-compose-fullnode.yaml

sudo wget -O fullnode.yaml https://raw.githubusercontent.com/aptos-labs/aptos-core/main/docker/compose/aptos-node/fullnode.yaml

```

{% endcode %}

### 编辑全节点配置文件

文件：`fullnode.yaml`

修改第 17 行的内容为您的您的验证者节点ip或域名，其他的内容无需修改：

{% code lineNumbers="true" %}

```yaml
base:
  role: "full_node"
  data_dir: "/opt/aptos/data"
  waypoint:
    from_file: "/opt/aptos/genesis/waypoint.txt"

execution:
  genesis_file_location: "/opt/aptos/genesis/genesis.blob"

full_node_networks:
- network_id:
    private: "vfn"
  listen_address: "/ip4/0.0.0.0/tcp/6181"
  seeds:
    00000000000000000000000000000000d58bc7bb154b38039bc9096ce04e1237:
      addresses:
      - "/ip4/修改成您的验证者节点ip或域名/tcp/6181/noise-ik/f0274c2774519281a8332d0bb9d8101bd58bc7bb154b38039bc9096ce04e1237/handshake/0"
      role: "Validator"

- network_id: "public"
  discovery_method: "onchain"
  listen_address: "/ip4/0.0.0.0/tcp/6182"
  max_inbound_connections: 100
  identity:
    type: "from_file"
    path: "/opt/aptos/genesis/validator-full-node-identity.yaml"

api:
  enabled: true
  address: "0.0.0.0:8080"
```

{% endcode %}

### 拷贝 genesis.blob、waypoint.txt 和 fullnode key 文件

{% hint style="warning" %}
本步骤中的命令在验证者节点上执行！！！
{% endhint %}

{% code lineNumbers="true" %}

```bash
 # 进入验证者节点操作的目录（如果不是，请自行修改）
 cd /root/aptos-node/testnet3
 
 sudo scp genesis.blob root@全节点ip:/root/aptos-node/movemove/testnet3/
 
 sudo scp waypoint.txt root@全节点ip:/root/aptos-node/movemove/testnet3/
 
 sudo scp keys/validator-full-node-identity.yaml root@全节点ip:/root/aptos-node/movemove/testnet3/keys/
 
```

{% endcode %}

### 启动全节点

{% hint style="info" %}
需要放行的端口：80、9101、6182
{% endhint %}

```bash
sudo docker-compose -f docker-compose-fullnode.yaml pull

sudo docker-compose -f docker-compose-fullnode.yaml up -d
```

### 节点检测

```bash
sudo curl 127.0.0.1/v1
```

如果返回 error，或自行查看节点日志出现大量 401，则节点是有问题的，可以参考的解决方案：

1. 验证者节点未放行 6181 端口；
2. 未使用验证者节点的 genesis.blob、waypoint.txt 和 fullnode key 文件；
3. 验证者节点搭建过程中，未正确填写全节点 ip 和端口。

对教程有建议？欢迎加入 MoveMove 社区：<https://discord.gg/Vsj7Jwny9G>


# 加入测试网

AIT 教程，Docker 方式加入测试网

{% hint style="danger" %}
注意：

1. 本教程仅用于使用 Docker 方式，且为验证者节点。
2. <mark style="color:green;">欢迎加入 MoveMove 社区</mark>：<https://discord.gg/sUmS7pm2q2>
   {% endhint %}

## 一、初始化质押池

在 AIT3 中，我们将提供 UI 支持以允许所有者管理质押池，请参阅此处的详细信息，如果您已经通过 UI 完成了此操作，则可以忽略此步骤并跳转到“[运行节点](#yi-yun-hang-jie-dian)”。

{% hint style="warning" %} <mark style="color:orange;">本步骤需要等到测试币发放才能执行。(若您没有收到，请关注邮件耐心等待)</mark>
{% endhint %}

### 1.1 切换钱包网络 Testnet 并检查余额

{% hint style="danger" %} <mark style="color:red;">需要先安装 Petra 钱包插件（必须更新到 0.1.8）</mark>，并导入之前注册时使用的钱包地址。参考教程：[Petra 使用教程](/jiao-cheng/petra-qian-bao-cha-jian-an-zhuang-shi-yong)
{% endhint %}

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FiIiLtVs4VEBoIjjQ6FFp%2Fimage.png?alt=media&amp;token=f8c1136c-e002-4b59-945f-8e49b238403a" alt=""><figcaption></figcaption></figure>

切换到测试网网络后，返回 Home 就能看到余额了。

### 1.2 参与质押

Aptos 治理页面（需切换到相应的测试网络）：<https://explorer.devnet.aptos.dev/proposals/staking>

* Staking Amount: 输入100000000000000，需要小于账户余额，并留足够的 gas。
* Operator Address: 节点名称/operator.yaml 文件中的 operator\_account\_address。

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FgtuKhvoP62dwUHwLfUfd%2Fimage.png?alt=media&amp;token=01976b9f-1c3a-41d2-bbd3-17946dc1f71e" alt=""><figcaption></figcaption></figure>

* Voter Address: 您的钱包地址。

填写质押的数量和操作者地址、投票者地址（您的钱包地址），并提交（点击提交按钮后，会有弹窗交易签名，点击“Approve”即可）：

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FjMhwuAwbt89G8aIqxnkv%2Fimage.png?alt=media&amp;token=c057f2b2-ddec-4080-9039-0b23744ba872" alt=""><figcaption></figcaption></figure>

质押成功页面（可以修改和增加锁定时长）：

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fk8q2kJYarAlqvuPwWvUT%2Fimage.png?alt=media&amp;token=72e2315d-05dd-4b27-b22d-c76bdf6db7bf" alt=""><figcaption></figcaption></figure>

### 1.3 给您的 Operator 地址发送一些 APT 测试币

{% hint style="danger" %}

* 这步是必须的，后面需要检查账户余额，否则会返回错误！！
* Operator 地址是上一步中的 Operator Address。
  {% endhint %}

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FJPGJR9ZnKUINxGQFkr0c%2Fimage.png?alt=media&amp;token=792ee65e-4129-4cc0-bb31-a1832fda0e58" alt=""><figcaption></figcaption></figure>

## 二 、运行节点

### 备份所有文件

防止发生异常时无法恢复，请先备份如下文件，文件目录如下：

```
docker-compose.yaml
layout.yaml
您的节点名称目录
keys 目录
```

### 进入节点操作目录

本教程使用的目录为 `~/aptos-node/testnet3`，若您使用的其他教程，请自行修改命令。

```shell
cd ~/aptos-node/testnet3

```

### 关闭节点并删除数据卷

```shell
sudo docker-compose down --volumes

```

### 下载 genesis、waypoint 和 测试网 编排文件

<pre class="language-bash"><code class="lang-bash"><strong>sudo wget -O genesis.blob https://github.com/aptos-labs/aptos-genesis-waypoint/raw/main/testnet/genesis.blob
</strong>sudo wget -O waypoint.txt https://raw.githubusercontent.com/aptos-labs/aptos-genesis-waypoint/main/testnet/waypoint.txt
sudo wget -O docker-compose.yaml https://raw.githubusercontent.com/jiangydev/aptos-node/main/testnet/AT/docker-compose.yaml

</code></pre>

### 修改 validator-identity.yaml 中的 account\_address

文件路径：`keys/validator-identity.yaml`

只需要修改 account\_address，更换成您的钱包地址：

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMxjVZL2rkQUDJFizJfRs%2Fimage.png?alt=media&amp;token=e252fb31-c421-49ec-9113-ff58615d8dce" alt=""><figcaption></figcaption></figure>

### 启动节点（Run node）

```bash
sudo docker-compose pull
sudo docker-compose up -d

```

{% hint style="warning" %}
如果您的节点端口不通，请关闭服务器防火墙，并在云服务器提供商的安全策略中放行如下端口：

验证者节点（本教程只提供验证者节点搭建）：

* 6180：validator 的 peer 通信端口；
* 6181

已关闭 80 API 端口及 9101 指标端口的外网访问，本地可访问。
{% endhint %}

### 检查节点

```shell
# 查询 chain id，应该返回 47
sudo curl 127.0.0.1:80/v1
```

## 三、加入验证者集合

### 初始化 Aptos CLI

您的操作者私钥 `account_private_key` 可以从 `keys/private-keys.yaml` 文件中获得。

```shell
# 这个命令中的私钥需要替换！！
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.4 \
  sh -c "cd /data/aptos-cli && rm -rf .aptos && aptos init --profile ait3-operator --private-key 您的私钥 --rest-url https://testnet.aptoslabs.com --skip-faucet"
```

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FxnA95lpUvDVrJqPYUSy2%2Fimage.png?alt=media&amp;token=d9ff8753-070f-45d2-965f-03d68afa62ba" alt=""><figcaption></figcaption></figure>

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F4clktk5kzhmrNZTyFXsk%2Fimage.png?alt=media&amp;token=095a7c87-86fb-45f9-a123-7597738589e0" alt=""><figcaption></figcaption></figure>

### 通过 Aptos CLI 检查余额

{% hint style="warning" %}
如果这一步执行返回异常：API error: API error Error(AccountNotFound): Account not found by Address，请参考 1.3 给您的 Operator 地址发送一些 APT 测试币。
{% endhint %}

```shell
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.4 \
  sh -c "cd /data/aptos-cli && aptos account list --profile ait3-operator"

```

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F4t56xX1eQ0p02lj0B51w%2Fimage.png?alt=media&amp;token=59b974af-4e5b-4a9a-9468-dc15a2dedb66" alt=""><figcaption></figcaption></figure>

### 更新链上Validator网络地址

* 注意修改命令中的验证者配置文件为“您的节点名称/operator.yaml”，这里我使用的 MoveMove/operator.yaml。
* 注意修改所有者地址：您的钱包地址！！！

```shell
# 需要修改验证者配置文件：您的节点名称/operator.yaml
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.5 \
  sh -c "cd /data/aptos-cli && aptos node update-validator-network-addresses --pool-address 您的钱包地址 --operator-config-file MoveMove/operator.yaml --profile ait3-operator --assume-yes"
```

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FfMgHLxRmvb1O0gDSxuW5%2Fimage.png?alt=media&amp;token=bda12235-d526-45ca-af84-bdeea38f6af8" alt=""><figcaption></figcaption></figure>

### 更新链上验证者共识 key

```bash
# 需要修改验证者配置文件：您的节点名称/operator.yaml
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.5 \
  sh -c "cd /data/aptos-cli && aptos node update-consensus-key --pool-address 您的钱包地址 --operator-config-file MoveMove/operator.yaml --profile ait3-operator --assume-yes"
```

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F8GvV1rp8ScEYVPs25DHS%2Fimage.png?alt=media&amp;token=b293ff17-ac21-421e-ada4-457853e9c020" alt=""><figcaption></figcaption></figure>

### 加入验证者集合

```bash
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.5 \
  sh -c "cd /data/aptos-cli && aptos node join-validator-set --pool-address 您的钱包地址 --profile ait3-operator --max-gas 5000 --assume-yes"

```

<figure><img src="https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FukbXCeROO5rK1pDCS0cS%2Fimage.png?alt=media&amp;token=61fd871e-a5f9-4049-b221-2d4aeb48bbda" alt=""><figcaption></figcaption></figure>

### 检查验证者集合

#### 方式一

<https://explorer.devnet.aptos.dev/account/><你的地址>?network=testnet

可以看到 Stake 相关的资源和交易。

#### 方式二

```bash
# 需要先安装 jq，如果已安装，可跳过
sudo apt install -y jq
```

在安装 jq 后，可以查询**加入中的验证者**：

```bash

sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.4 \
  sh -c "cd /data/aptos-cli && aptos node show-validator-set --profile ait3-operator" \
  | jq -r '.Result.pending_active' \
  | grep '您的钱包地址'

```

查询**已加入的验证者**：

```bash

sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.4 \
  sh -c "cd /data/aptos-cli && aptos node show-validator-set --profile ait3-operator" \
  | jq -r '.Result.active_validators' \
  | grep '您的钱包地址'

```

## 四、验证节点连接

一旦您的验证器节点加入验证器集，您可以按照以下步骤验证正确性：

### 验证 Peer

验证您的节点是否正在连接到测试网上的其他对等点，`127.0.0.1`（如果部署在云上，请替换为您的验证器 IP/DNS）

```shell
curl 127.0.0.1:9101/metrics 2> /dev/null | grep "aptos_connections{.*\"Validator\".*}"

```

该命令将输出验证器节点的入站和出站连接数。

只要其中一个指标大于零，您的节点就会连接到测试网上的至少一个对等节点。

### 是否连接到 AptosLabs 节点

您还可以检查您的节点是否连接到 AptosLabs 的节点，替换`<Aptos Peer ID>`为 Aptos 团队共享的 peer ID。

> 获得官方信息：<https://github.com/aptos-labs/aptos-ait3>

```shell
curl 127.0.0.1:9101/metrics 2> /dev/null \
  | grep "aptos_network_peer_connected{.*remote_peer_id=\"f326fd30\".*}"

```

### 检查共识

一旦您的节点状态同步到最新版本，您还可以检查共识是否正在取得进展，以及您的节点是否正在提议

```shell
curl 127.0.0.1:9101/metrics 2> /dev/null \
  | grep "aptos_consensus_current_round"

curl 127.0.0.1:9101/metrics 2> /dev/null \
  | grep "aptos_consensus_proposals_count"

```

您应该期望看到这个数字不断增加。

<mark style="color:green;">欢迎加入 MoveMove 社区</mark>：<https://discord.gg/sUmS7pm2q2>


# 离开激励测试网

AIT3 教程，Docker 方式，MoveMove 社区

{% hint style="danger" %}
注意：

1. 本教程仅用于使用 Docker 方式，且为验证者节点。
2. <mark style="color:green;">欢迎加入 MoveMove 社区</mark>：<https://discord.gg/sUmS7pm2q2>
   {% endhint %}

首先，感谢所有伙伴帮助 Aptos 完成 AIT3 测试。

节点可以选择随时设置验证器，或者<mark style="color:orange;">当验证器帐户没有足够的质押时，它会自动发生</mark>。要离开验证器集，您可以执行以下步骤：

### 进入节点操作目录

本教程使用的目录为 `~/aptos-node/testnet3`。

```shell
cd ~/aptos-node/testnet3
```

如果<mark style="color:orange;">您是第一次使用 MoveMove 社区教程</mark>，在执行后面的步骤前，需要先初始化 Aptos CLI，参考：[加入测试网](/jiao-cheng/ce-shi-wang-testnet/she-qu-jiao-cheng/jia-ru-ce-shi-wang#chu-shi-hua-aptos-cli)

### 离开验证器集（将在下一个 epoch 生效）

{% hint style="danger" %}
如果您的质押没到没到解锁时间，执行以下命令将提示错误；

此时，您需要等待解锁时间后才能退出验证者集。
{% endhint %}

```bash
sudo docker run --rm \
  -v $(pwd):/data/aptos-cli \
  jiangydev/aptos-cli:v0.3.3 \
  sh -c "cd /data/aptos-cli && aptos node leave-validator-set --profile ait3-operator --pool-address 您的钱包地址 --max-gas 5000"

```

### 最后安全的关闭节点

{% hint style="danger" %}
如果您要关闭服务器，请备份当前目录下的所有文件（除了 waypoint.yaml 和 genesis.blob）。
{% endhint %}

```bash
sudo docker-compose down -v

```

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FXUZuwzInSzI4raH4hXOA%2Fimage.png?alt=media\&token=2a97afcd-28e0-4c62-976f-9e7ac4eba5f9)

<mark style="color:green;">欢迎加入 MoveMove 社区</mark>：<https://discord.gg/sUmS7pm2q2>


# 博客


# Block-STM：我们如何在 Aptos 区块链上每秒执行超过 16 万笔交易

世界一流的工程与 Aptos 的前沿研究相得益彰。 有关详细信息，请参阅 Block-STM 论文和 Aptos 代码库。

作者：[**Alexander Spiegelman**](https://twitter.com/sashaspiegelman) **/** [**Rati Gelashvili**](https://twitter.com/rgelash)

TL;DR:

我们设计并实现了一个高效、多线程、内存中的并行执行引擎，通过利用预设的事务顺序并将软件事务内存技术与新颖 协作时间表。

## 挑战 <a href="#f4bf" id="f4bf"></a>

智能合约执行是区块链的主要吞吐量瓶颈。 在提出区块并就其顺序达成一致后，验证者必须在有序区块中执行交易。 至关重要的是，验证者必须达到相同的最终状态，这必须对应于交易的某些顺序执行。 由于缺乏基础解决方案，当前的区块链要么按顺序执行，要么需要令人尴尬的并行工作负载（即没有内部冲突）以提高性能。 顺序执行不能很好地扩展，并且在考虑广泛的智能合约时假设所有交易都是可交换的是不现实的。 事实上，由于潜在的性能攻击、访问流行的合约（例如，由于拍卖和套利等经济机会），区块链中的交易可能会出现大量访问冲突。

## 目标 <a href="#ffcb" id="ffcb"></a>

考虑到工作负载中的实际访问冲突，设计一个并行执行引擎，以提取可能的最大固有加速。 为了获得最佳编程和用户体验，算法应该对用户透明。 一些区块链并行执行引擎采用的另一种方法是强制用户预先声明依赖关系，这严重限制了事务可以做什么，并且可能需要分解或重试事务。 相反，为了避免产生此类成本和编程烦恼，我们的并行执行引擎的系统设计目标是在内部管理所有冲突并自动适应工作负载。

## STM 方式 <a href="#efdc" id="efdc"></a>

[软件事务内存 (STM)](https://link.springer.com/article/10.1007/s004460050028) 库开创的一种学术方法是检测内存访问以检测和管理冲突。 具有[乐观并发控制](https://en.wikipedia.org/wiki/Optimistic_concurrency_control)的 STM 库记录执行期间的内存访问，在执行后验证每个事务，并在验证出现冲突时中止并重新执行事务。 然而，由于所需的冲突簿记和中止，与定制解决方案相比，STM 库经常受到性能限制，因此很少在生产中部署。

## 区块链用例 <a href="#id-4a99" id="id-4a99"></a>

过去表明，当 STM 应用于特定用例时，它们的性能可以显着提高。事实上，区块链用例的三个重要观察指导了 Block-STM 的设计。

* **无需单独提交事务**：与通用 STM（每个线程有无限的事务流基于可能在任意时间到达的查询提交）相比，在区块链中，状态通常更新每个块。这允许 Block-STM 避免单独提交事务的同步成本。相反，Block-STM 通过轻同步延迟提交块中的所有事务。此外，垃圾收集很简单，因为可以在块之间清理内存。
* **VM 为乐观内存访问提供安全性**：交易以智能合约语言（如  [Move](https://move-book.com/)  和  [Solidity](https://docs.soliditylang.org/en/v0.8.13/)）指定，并在封装其执行并确保安全行为的虚拟机中运行。这很好地分离了抽象，并允许 Block-STM 在并行推测执行期间避免处理不一致状态的后果（称为不透明度的属性）。
* **预定义的顺序减少了同步**：通常，STM 库以非确定性为目标，并将确定性视为系统中的限制，从而阻碍了性能。这使得它们不适合区块链用例，因为执行相同块的验证器可能会导致不同的最终状态。然而，在 Block-STM 中，确定性被认为是一种性能优势。事实上，最终结果保证与以固定、预设顺序执行的交易相匹配，并且该约束被用于系统的优势。这是可能的，正如之前在 [Bohm](https://arxiv.org/abs/1412.2324) 论文中在数据库上下文中所指出的那样，因为就特定的序列化达成一致会减少执行期间所需的同步量。例如，如果事务 tx5 和事务 tx9 之间存在冲突，则 tx9 将等待 tx5——否则，如果没有顺序，执行这些事务的线程将需要打破平局。因此，在直观的层面上，通用 STM 将解决更难的问题（即，一种共识形式），Block-STM 针对更简单的问题（即，它只需要执行交易）。

## 核心技术 <a href="#e246" id="e246"></a>

Block-STM 将已知技术与新颖想法相结合：

* **乐观并发控制**：事务在并行和经过验证的执行后乐观地执行。不成功的验证会导致重新执行。由于预设的顺序，验证不是相互独立的，并且必须在逻辑上按顺序发生。与之前的工作不同，成功的验证并不意味着可以提交事务。相反，事务验证失败意味着所有更高级别的事务只有在之后成功验证后才能提交。
* **多版本数据结构**：Block-STM 使用多版本数据结构来避免写-写冲突。所有对同一位置的写入都与它们的版本一起存储，其中包含它们的事务 ID 和写事务被乐观地重新执行的次数。当事务 tx 读取一个内存位置时，它会从多版本数据结构中获取按预设顺序出现在 tx 之前的最高事务写入该位置的值以及关联的版本。
* **验证**：在执行期间，事务记录一个读集和一个写集。在验证期间，读取读取集中的所有内存位置，并将返回的版本与存储在读取集中的相应版本进行比较。
* **协作调度**：Block-STM 引入了一个协作调度器来协调线程之间的验证和执行任务。由于预设顺序规定事务必须按顺序提交，因此事务执行的成功验证并不能保证它可以被提交。这是因为块中较早事务的中止和重新执行可能会使读取集无效并需要重新执行。因此，事务和执行线程之间的静态映射不起作用。相反，协作调度程序优先考虑较低事务的执行和验证任务。然而，众所周知，有序集和优先级队列在多核环境中难以扩展。 Block-STM 使用基于计数的方法回避了这个问题，这可以通过预设排序和事务的紧凑索引来实现。
* **动态依赖估计**：Block-STM 利用预设顺序来显着避免中止，这是 STM 系统性能游戏的名称，因为中止可能级联并导致过多的工作浪费。当验证失败时，事务最后一次执行的写入集用于通过将其在多版本数据结构中的所有写入标记为 ESTIMATION 来估计依赖关系。当另一个事务从多值数据结构中读取一个 ESTIMATION 值时，它可以等待依赖关系被解决——虽然没有估计，它会继续但​​很可能（如果写入 ESTIMATION 的事务确实写入相同下一次重新执行中的位置）以稍后验证失败并中止。与通过从预块状态并行地预先执行所有事务来生成写入估计的教科书方法相比，我们的方法有两个好处：（a）仅在需要时生成估计（不是针对每个事务），以及（b ) 估计通常基于比块开始时更新鲜的状态。

## 评估 <a href="#id-881f" id="id-881f"></a>

我们在 Aptos 开源代码库中的安全 Rust 中实现了 Block-STM，依靠 Rayon、Dashmap 和 ArcSwap crates 实现并发。 我们使用非平凡的点对点移动事务（8 次读取和 5 次写入）评估了系统。 在下图中，我们将 Block-STM 与块的顺序执行进行了比较。 每个区块包含 10k 笔交易，账户数量决定了冲突和争用的程度。 在低争用情况下，Block STM 归档比使用 32 个线程的顺序执行提高 16 倍，而在高争用情况下，Block-STM 归档超过 8 倍加速。 重要的是，当工作负载本质上是连续的时，Block-STM 会产生少量开销。 总体而言，Block-STM 能够动态且透明地（无需用户提示）从工作负载中提取固有的并行性。 可以在[论文](https://arxiv.org/pdf/2203.06871.pdf)中找到与相关工作的详细比较。&#x20;

### 不同争用级别的 Block STM 性能 <a href="#bab3" id="bab3"></a>

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FxmdHfYQNgwvla7bZoPq5%2Fimage.png?alt=media\&token=72d6c47d-abda-43e1-bece-d8e98a2c2a33)

## 那么我们如何获得这种性能呢？ <a href="#bb3e" id="bb3e"></a>

协作调度器与多版本技术一起允许 Block-STM 利用预设的事务顺序来估计依赖关系并显着减少浪费的工作量。协作调度器是算法的关键部分，包含大部分性能关键逻辑。除其他外，它确保：

* 每个中止的事务都会重新执行，但同一个事务永远不会被多个线程同时执行。
* 如果重新执行事务，则必须重新验证所有更高级别的事务。结果，相同的事务执行可能会由不同的线程同时验证，但最多可以中止它。
* 解决依赖关系后恢复遇到依赖关系的事务。
* 所有事务最终都被提交。

挑战在于以尽可能少的同步开销确保上述内容。跟踪执行和验证任务的一种简单但昂贵的方法是拥有两个优先级队列（或有序集以避免重复）。一旦解决了依赖关系或中止了验证，就会将相应事务的执行任务添加到执行队列中。类似地，一旦一个事务被执行，就会按照预设的顺序为所有更高的事务创建验证任务，并添加到验证队列中。

## 高效的并发有序集 <a href="#id-6bda" id="id-6bda"></a>

相反，Block-STM 利用预设的交易顺序并使用两个原子计数器。

这些计数器跟踪需要执行或验证的任何事务的下限索引。结合了解每个事务状态的方法，这些索引可用于有效地模拟来自朴素方法的有序集语义。线程反复尝试通过获取并递增具有较低索引的计数器来获取任务，并读取状态以检查相应的事务是否已准备好执行（或验证，取决于计数器）。 Fetch-and-increment 也自然地将线程分散到不同的索引，确保状态信息不会被大量竞争，这允许我们使用互斥锁并简化实现（无锁实现是可能的，但基准测试并没有显示出显着的改进）。

下图给出了交易的可能状态。下图中的参数 i 是事务被重新执行的次数。

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FOuq5JN1V3kYdqpghPdaK%2Fimage.png?alt=media\&token=ca97204f-38a6-44b8-8ace-d35685094b6f)

我们还使用锁来同步依赖关系列表，但是，如果不小心，协作调度程序必须避免一些微妙的竞争仍然可能 - 精彩的细节在论文中。

## 惰性提交机制 <a href="#id-1db5" id="id-1db5"></a>

Block-STM 提交整个区块以消除跟踪单个事务何时可以安全提交的同步成本。简而言之，当满足以下所有条件时，可以提交整个块​​：&#x20;

1. 执行和验证索引都达到块大小。
2. 没有正在进行的验证和执行任务。
3. 所有交易的状态都是 EXECUTED。

正如我们在 #way-too-rigorous-for-a-systems-result 证明中所证明的那样，Block-STM 算法中的前两个条件暗示了第三个条件。为了原子地验证 (1) 和 (2)，我们引入了两个额外的计数器。第一个计数器跟踪正在进行的任务的数量。在代码中的错误位置增加或减少这个计数器太容易了，而且从不观察 (2)。为避免此类错误，我们使用资源获取即初始化 (RAII) 模式将计数器与任务守卫相结合，确保在分派任务之前和完成之后分别增加和减少它。

为了以原子方式读取此计数器以及跟踪执行和验证索引的计数器，我们使用双重收集技术。这需要引入第四个原子计数器来跟踪执行和验证计数器减少的次数。然后，我们收集所有计数器的值两次（顺序很重要！）如果在两种情况下都满足条件（1）和（2），那么（正如我们所证明的），在两者之间的某个时间，条件（1）和(2) 确实同时满足。在这种情况下，整个块被安全提交，因为不再需要或将创建任何验证和执行任务。

如果您像我们一样热衷于设计算法、将它们付诸实践并对 Web3 的未来产生真正的影响，请联系我们——我们正在 [招聘](https://boards.greenhouse.io/aptoslabs/)。


# 生态


# Aptos 宣布资助计划！

我们计划进一步加速 Aptos 生态系统的快速增长

发布时间：2022年6月29日

当我们在今年 3 月推出开发者测试网时，Aptos 作为安全、可扩展和可升级的第 1 层区块链首次亮相，区块链由 Move 提供支持。不到四个月后，开发人员已经执行了数百万笔交易，数以万计的节点启动了，超过 1,500 人 fork 了 aptos-core 仓库。今天，我们宣布有超过 100 个项目在网络上构建，用例涵盖 DeFi、NFT、游戏等。 [Pontem Network](https://twitter.com/PontemNetwork), [the Macalinao brothers](https://protagonist.co/), [Nutrios](https://twitter.com/nutriosProject), [PayMagic](https://twitter.com/paymagic_), [MartianDAO](https://twitter.com/martian_wallet), [Solrise](https://twitter.com/SolriseFinance) 等团队还有更多人已经在 9 月底左右为 Aptos 主网编写代码、交付和准备——仅几个月后！

我们很高兴正式宣布 Aptos 资助计划，为世界各地的团队、个人和创作者提供非稀释性资金，以进一步加速 Aptos 生态系统的发展。

## 拨款申请和审查程序 <a href="#id-0898" id="id-0898"></a>

申请人可以申请以下建议的资助类别之一。这些建议是针对当前生态系统需求量身定制的，旨在帮助申请人了解在 Aptos 上构建什么。鼓励申请人申请可以进一步造福 Aptos 生态系统的赠款，即使未在以下列表中找到。

### 资助类别： <a href="#id-98c6" id="id-98c6"></a>

* 开发人员工具、SDK、库、文档、指南、教程
* 用于开发、治理、DeFi 和 NFT 的工具和框架
* 核心协议贡献：代币标准、库、协议升级等。
* 开源和公共产品
* 教育举措
* 应用程序（例如 DeFi、NFT、社交网络、游戏、DAO、支付、桥接）

### 初始标准： <a href="#b5bd" id="b5bd"></a>

申请人必须满足以下条件才有资格获得 Aptos 资助：

* 项目必须为生态系统提供新的实质性价值。我们正在寻找能够为 Aptos 区块链带来激情和新想法的强大、有创意的项目
* 项目必须是开源的，或承诺开源的时间表
* 团队可以公开保持匿名，但需要通过 Aptos 的身份筛选
* 我们将在全球范围内提供赠款，受制裁司法管辖区的个人或实体除外

### 申请建议指南 <a href="#a5ca" id="a5ca"></a>

申请人在完成申请时将被要求上传拨款提案。我们创建了一个供团队遵循的[提案模板。](https://docs.google.com/document/d/1FbynBbIuXh2o85lFe3nNeyeG1xwjaGOUcyKhnFKMecI/edit)我们要求提供以下信息：

* 项目名称
* 申请资助的公司或个人名称+联系方式
* 项目描述
* Aptos 生态系统的价值主张
* 可交付成果
* 团队+过往经历
* 技术架构概述（如果适用）
* 主要风险
* 财务和法律信息

### 审核流程 <a href="#id-7ce6" id="id-7ce6"></a>

* Aptos 团队会滚动审核所有提交的拨款申请。
* Aptos 生态系统团队的成员将在收到第一个应用程序草案后联系、提供反馈并请求更改。
* 面试后，申请人将收到关于他们的拨款提案的决定。
* 然后，拨款协议将与团队共享，以执行并继续获得 Aptos 的资助。

请注意，赠款资金以美元分配。未来，我们可能会选择分发代币，以帮助激励各方的长期生态系统增长。

## 如何申请 Aptos 资助 <a href="#c2fc" id="c2fc"></a>

通过访问我们的[申请表单](https://docs.google.com/forms/d/e/1FAIpQLSenkcdYauqAsIbEJJ6x-x1VoJkD7FnD_f5r33izcNVFP5ni1w/viewform)开始！密切关注未来的举措、生态系统基金和漏洞赏金计划。有关 Aptos 资助计划的问题和更多信息，请随时联系 <grants@aptoslabs.com>。


# Aptos 生态项目汇总


# 聚合器


# Hippo Labs

推特：<https://twitter.com/hippolabs__>

Discord：<https://t.co/7gKFRqc3qi>

Github：<https://github.com/hippospace/move-to-ts>


# Defi


# Pontem Network

Pontem 是一个产品工作室，致力于由区块链驱动的全球金融包容性

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMcTAAncC15kt4TAOKlBP%2Fimage.png?alt=media\&token=06994fe9-c65b-47df-b823-0a9941fca1de)

Pontem 是一个产品工作室，致力于由区块链驱动的全球金融包容性。 我们与 Aptos 合作构建基础 dApp 和其他基础设施，以支持 Aptos 网络的采用，例如开发工具、EVM 实施、AMM 等。

Pontem 已启动大使计划：<https://pontem.network/ambassador-program>

现在在 devnet 上启动：<https://liquidswap.com/#/>

主页：[https://pontem.network](https://pontem.network/)

推特：<https://twitter.com/PontemNetwork>

Discord：<https://discord.gg/uBXNseF7WQ>


# Vial Protocol

Vial Protocol 是 Aptos 上的一种自主算法流动性协议

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMRI4an5DCoEwpT85lC9n%2Fimage.png?alt=media\&token=20d2fd63-2703-4eee-9705-e05b4c97802e)

Vial Protocol 是 Aptos 上的一种自主算法流动性协议。

主页：[https://www.vial.fi](https://www.vial.fi/)

推特：<https://twitter.com/vialprotocol>

Discord：<https://discord.gg/tzbNJhAuWa>


# 1KX Protocol

1KX 协议是 AMM + DEX + 借贷协议 + 稳定币的新混合体

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FQq2RIOoaTFrDciLMkPtD%2Fimage.png?alt=media\&token=fb5b3d7b-93f8-4057-a33b-ee4eb8083c02)

1KX 协议是 AMM + DEX + 借贷协议 + 稳定币的新混合体。

主页：[https://1kx.exchange](https://1kx.exchange/)

推特：<https://twitter.com/1kxprotocol>

Discord：<https://discord.gg/uBjzvRnydt>


# Seam Money

具有跨顶级 celo defi 协议、风险不利收益聚合的分散存款的捆绑池

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FghMBFt1ZE5SiizAhLRss%2Fimage.png?alt=media\&token=e786873f-ccca-4166-b7eb-b1d67ed89df4)

具有跨顶级 celo defi 协议、风险不利收益聚合的分散存款的捆绑池。

主页：[https://www.seam.money](https://www.seam.money/)

推特：<https://twitter.com/SeamMoney>

Discord：<https://discord.gg/sArdVckFx9>

GitHub：<https://github.com/Jack-kelly-22/seam>


# Aries Markets

Aptos 上的去中心化保证金交易

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FYjlwhF9DWXsb20DUUfP4%2Fimage.png?alt=media\&token=0352fdad-f09b-4afd-bd6f-9bb3f524f9f8)

推特：<https://twitter.com/AriesMarkets>


# Empo Finance

Aptos 上最安全的 DEX

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F1ZbuPJY8C5lYtdCuXt0y%2Fimage.png?alt=media\&token=78a101d0-759b-4629-a5dd-5311f1e28ca7)

推特：<https://twitter.com/Empo_Fi>


# Ultima protocol

Aptos 的流动性中心

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fi1mATqrPyQoMp1P8CXgn%2Fimage.png?alt=media\&token=90bf7de1-5d10-4ecd-a3a1-505977dd52cc)

推特：<https://twitter.com/ultimaprotocol>


# Econia

Econia 是一个中央限价订单簿 (CLOB)，是股票市场等金融机构使用的基本金融工具

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FqT7Ijug0SSrMKsGsvmHS%2Fimage.png?alt=media\&token=bf7b9a81-6eaa-4c0c-98e8-3da9d863f69b)

Econia 是一种协议，允许世界上的任何人以任何他们想要的价格与世界上的任何其他人交易数字资产。 更具体地说，Econia 是一个中央限价订单簿 (CLOB)，是股票市场等金融机构使用的基本金融工具，除了与纽约证券交易所或纳斯达克不同，Econia 是开源和去中心化的。

**Econia 正在开发中**

Econia 目前正在开发中，已交付给 Aptos 区块链的第一个迭代。 此版本 1 最小可行原型（v1 MVP）引入了链上交易的一项关键创新，即 Econia 的原子匹配引擎，该引擎在下达交易期间以最终性结算市场订单。 没错，Econia 的 v1 MVP 实现了全球首个链上原子匹配引擎，欢迎各位建设者亲自尝试！ 如果您是一名 builder 并且对与 Econia 合作感兴趣，请首先查看设计概述，因为它提供了一个技术细分，可为系统设计提供一些背景信息。 然后尝试使用 Econia，看看我们可以一起构建什么！

推特：<https://twitter.com/EconiaLabs>

Discord：<https://discord.gg/gxhaJt6ZWc>

Medium：<https://medium.com/econialabs>

GitHub：<https://github.com/econia-labs/econia/blob/main/doc/doc-site/welcome.md>


# Zaptos Finance

Aptos 上的流动性质押平台

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FL0C2eveJTxQn5ouec66Y%2Fimage.png?alt=media\&token=cf9aecde-fcb1-4369-88c7-f46117da67b1)

Zaptos 为 Aptos 创建了流动性堆叠平台。

堆叠 APTOS 代币是权益证明网络的关键，堆叠将提供收入作为将您的 APTOS 委托给验证者的奖励。 但是现在您已经质押了您的 APTOS，它们被锁定了一段时间，您无法使用它们。 Zaptos 已经出现，它将允许您提供 APTOS，我们将代表您将其委托给验证者，作为交换，您将收到 zAPTOS，这是一种 APTOS 的流动衍生品，随着时间的推移积累价值（它的价值超过 APTOS，基于 APR 到 APTOS 速度）。 现在你的 APTOS 可以增长，你可以把 zAPTOS 放在你的钱包里，用它换成 APTOS，或者在许多新的 DeFi 平台上使用它。 我们还通过在许多验证者之间分配委托来帮助分散网络，以避免集中在其中的一些验证者上。

推特：<https://twitter.com/ZaptosFinance>

Discord：<https://discord.gg/tJp9Zjes9r>

Medium：<https://medium.com/@zaptosfinance>


# Laminar Market

Aptos 上的去中心化交易所

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FT48fAtoeGf4PqcbtbvDZ%2Fimage.png?alt=media\&token=eb821b26-43cb-4ee9-b0db-93f4f310bff7)

主页：[https://laminar.markets](https://laminar.markets/)

推特：<https://twitter.com/LaminarMarkets>

Discord：<https://discord.gg/P5ve9XZjaU>


# Thala Labs

Aptos 上的下一代 DeFi 流动性中心

主页：[https://www.thalalabs.xyz](https://www.thalalabs.xyz/)

该团队正在招募中。


# AptoSwap

Aptos 上的 Swap APP，以最优惠的价格安全轻松地交易加密货币

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FSmIHrx6NbBvZ3BFONKz0%2Fimage.png?alt=media\&token=1ba93caf-caf1-4052-b11b-489e00aef694)

推特：<https://twitter.com/AptoSwap_org>

团队介绍：我们是一个经验丰富的加密团队，拥有高水平的 MOVE 工程师和 mkt&运营团队，他们在其他区块链上建立了 200K+ 的社区。 技术负责人以前在 ETH、EOS、Solana 等方面具有开发经验。


# Aptoslend

Aptos 上的最佳的借贷平台

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F0wmZyH6qNeGxCOUQRQZD%2Fimage.png?alt=media\&token=176ef4eb-f42c-43b6-8870-364feb8da22a)

推特：<https://twitter.com/aptoslend>


# ASwap

一个基于社区的 DEX，用于安全快速地交换加密货币

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FhOIg76WYEw3BJcN1AJwO%2Fimage.png?alt=media\&token=a6a7b8e9-1ca3-4451-a5b5-a4c9fe50ebca)

推特：<https://twitter.com/aswaplabs>

Discord：<https://discord.gg/kVVjNHQpUz>


# 基础设施


# Aptos 域名服务

Aptos 上的去中心化身份

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Ftmld6PGg3HXt2curW99J%2Fimage.png?alt=media\&token=8ee97697-44da-4d31-b4c4-aad0315207d6)

主页：[https://www.aptosnames.com](https://www.aptosnames.com/)


# Nutrios

推特：<https://twitter.com/nutriosProject>


# Dialect

用于创建动态开源 dApp 通知、钱包聊天和兼容邮箱的工具包

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FHuRHydPWEWbX0Yszcqj8%2Fimage.png?alt=media\&token=07d5892f-d7a1-4ed0-b6be-a9fb2ae18dbd)

用于创建动态开源 dApp 通知、钱包聊天和兼容邮箱的工具包。

主页：[https://www.dialect.to](https://www.dialect.to/)

推特：<https://twitter.com/saydialect>

Discord：<http://discord.gg/dialect>


# Switchboard

Switchboard 允许构建者通过从任何 API 创建高性能数据馈送来释放 Solana 的强大功能

Switchboard 允许构建者通过从任何 API 创建高性能数据馈送来释放 Solana 的强大功能。

主页：<https://switchboard.xyz/>

推特：<https://twitter.com/switchboardxyz>

GitHub：<https://github.com/switchboard-xyz>


# NFT


# Topaz NFT Marketplace

探索艺术、文化和数字表达，Aptos 上的 NFT 市场

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F7QslAecwR9XnJNOu5P78%2Fimage.png?alt=media\&token=48cabaa9-9bec-4b89-ad12-67a35cf12fc1)

主页：<https://www.topaz.so/>

推特：<https://twitter.com/TopazMarket>

Discord：<https://discord.gg/d5NVSdXcdY>


# TokenMasksLabs

Aptos NFT 合集， 免费 Mint

推特：<https://twitter.com/TokenMasksLabs>

Discord：<https://discord.gg/D2X6CDQmNW>


# Clone Protocol

Aptos 上的可扩展合成资产

推特：<https://twitter.com/CloneProtocol>


# Aptos Ape Society

Aptos 上即将到来的第一个 2D 社交元宇宙，Aptos NFT 生态系统的文化中心

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FYDTF7VLQBg8SCMswDlQw%2Fimage.png?alt=media\&token=1ac00080-f2a1-4aff-acf7-83f1f35a1a83)

推特：<https://twitter.com/AptosApeSociety>


# 钱包


# Fewcha Wallet

Aptos 第 1 层区块链加密钱包

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FlCZ9IASW5GNYcsyFiidt%2Fimage.png?alt=media\&token=8f713cae-f9df-4836-8bd5-311a001e686a)

@AppsCyclone 的 Aptos 第 1 层区块链加密钱包。 浏览器钱包，能够安装在 Chrome、Brave、Edge 上。 计划 FireFox、IOS、Android。 该团队计划将钱包翻译成多种语言。 启动 Aptos 主网络后 - 与 Aptos 集成。

主页：<http://fewcha.app/>

推特：<https://twitter.com/FewchaWallet>

Discord：<https://discord.gg/YGkgGxP9aU>


# Martian Wallet

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FkYJPGoWo2XGQhBYFBED8%2Fimage.png?alt=media\&token=83f1a622-82b4-42b2-8e4a-be7707a24c51)

主页：<https://martianwallet.xyz/>

推特：<https://twitter.com/martian_wallet>

Discord：<https://discord.gg/f5YMZtJG2G>


# Volt.id wallet

一个易于使用和安全的无钥匙多链 web3 钱包

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FMRcB6KISEvLxc20WWfZn%2Fimage.png?alt=media\&token=d9a7412b-5055-48fa-b7ad-f5b8357a0945)

主页：[https://volt.id](https://volt.id/#/)

推特：<https://twitter.com/VoltWallet>


# ONTO Wallet

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FoetWoQL29XDERKxa8S02%2Fimage.png?alt=media\&token=96dc6afe-7bda-4656-92cd-0853d1087593)

主页：<https://onto.app/>

推特：<https://twitter.com/ONTOWallet>


# Hive Wallet

在 Aptos 上构建的适用于您和您的移动多重签名钱包

推特：<https://twitter.com/hivewithme>


# Blocto

跨链加密钱包 Blocto，一次性管理您的加密、dApp 和 NFT

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F7r0mGvqnRdwZn3x3mvxP%2Fimage.png?alt=media\&token=19b8a22f-7d03-4dfe-b4fe-ede344ef8aa6)

推特：<https://twitter.com/BloctoApp>

Discord：<https://discord.gg/blocto>


# 工具


# ChainIDE

便捷切换的云端多链智能合约 IDE，集成 Aptos 的 IDE

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FSO3iTg3lYA6COCw4JbGF%2Fimage.png?alt=media\&token=7709798d-b027-4883-8644-a4083c3e8915)

主页：[https://chainide.com](https://chainide.com/)

推特：<https://twitter.com/ChainIde>


# Paymagic

用于创建大规模支付、NFT/代币空投的工具

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FjfPGyhlrbw2CuBO0eWTh%2Fimage.png?alt=media\&token=da5bac7f-2ddd-40dd-b8ff-ad9fa9f7c6f8)

主页：<https://www.paymagic.xyz/>

推特：<https://twitter.com/paymagic_>


# Aptosphere

Aptos 的实时、索引链上活动。 使用 Aptosphere 数据平台构建您的 dapp、获得加密洞察等

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FQ7cR90LbKee7IAhRZSEU%2Fimage.png?alt=media\&token=3eba34ce-385f-474e-bb52-ec5a49cecb34)

主页：<https://www.aptosphere.com/>

推特：<https://twitter.com/Aptosphere>


# Saber Labs

Sabre Labs 为支持 SaberProtocol 生态系统的协议和开发工具

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2Fh3DXYnfd6K0DAuYhWAKq%2Fimage.png?alt=media\&token=d6206d0d-429b-467c-97b2-7d8002e0cba0)

主页：<https://saberlabs.org/>

推特：<https://twitter.com/Saber_HQ>

GitHub：<https://github.com/saber-hq>


# 问题（FAQ）


# 常见问题

### 一、社区教程和官方教程什么区别？

社区教程是官方教程的详细说明，便于 Aptos 用户流畅的搭建节点。

对社区教程的几点说明：

* 0 风险：社区教程不涉及任何三方脚本，无安全风险；

### 二、社区教程 执行命令无法生成私钥，提示 Illegal instruction (core dumped)

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FnCTNiKPdPCEYjSSU0jt2%2Fimage.png?alt=media\&token=566d67c0-a202-4a02-a8ac-a3bbb62669b8)

**原因**：很抱歉，因为您使用的 服务器/计算机 CPU 较老，无法运行在新 CPU 架构上编译的 Docker 镜像程序。

**解决方案**：

* 方案一：使用源码编译节点程序的方式运行。（编译时间较长，需耗费 1\~2 小时）
* 方案二：更换其他较新的 CPU 架构的 服务器/计算机，继续尝试。

### 三、只有开发人员才能运行全节点吗？

任何具有适当硬件要求的人都可以运行节点并参与其中！我们欢迎社会各界人士的加入和参与。

### 四、我可以运行验证者节点（Validator）吗？

目前 devnet（开发网）还不可以，但是一旦我们在第二季度启动了激励测试网，任何人都可以运行。

### 五、有区块浏览器吗？

devnet 区块浏览器：<https://explorer.devnet.aptos.dev>

### 六、全节点的建议硬件要求是什么？

4 核 CPU 和 16 GB 内存

### 七、全节点的最低硬件要求是什么？

2 核 CPU 和 8 GB 内存

### 八、编译源代码的最低硬件要求是什么？

16GB 内存

### 九、Aptos 代币名称（符号）是什么？发币了吗？

Aptos 目前还没公布经济白皮书，暂无代币名称（符号）。

还未发币，计划在 2022 Q3 发布主网。

### 十、全节点怎么查看出块数量？

全节点不参与共识，因此不会出块。

验证者（Validator）才参与共识并打包交易（也就是出块），全节点负责转发和同步交易。


# 如何分享自己的节点

前提：

* 您的节点需要有公网 IP；
* 服务器关闭防火墙；
* 在服务器供应商控制台放行 6180 和 8080 端口；

### 修改节点监听的地址

编辑 public full node.yaml 文件，将 `listen_address` 修改为 `0.0.0.0` ，使得能监听来自外部 IP 的流量。

如下图所示：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FZlA1rIUSe03jacHTezFM%2Fimage.png?alt=media\&token=6aa6c116-8f7c-4fce-a876-2f2e5077d00b)

### 映射节点端口

修改 docker-compose.yaml 文件，将 6180 暴露给公网。

新增一行 `- "6180:6180"` ，如下图所示：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FrsCCM9pkl5wfNTxfVuB3%2Fimage.png?alt=media\&token=aec615ab-4fe9-4cde-a577-f01344fff9c9)

### 重启节点

输入如下命令重启：

```
docker-compose down
docker-compose up
```

### 端口检测

可以使用[站长工具](https://tool.chinaz.com/port/)检测：

{% hint style="info" %}
若是开启状态，则可以继续下面的步骤分享。

如果是关闭状态，请检查节点运行状态、配置文件是否修改成功、防火墙是否关闭或放行 6180 端口。
{% endhint %}

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FfFSzg0P1PmPXkMOaSeGA%2Fimage.png?alt=media\&token=d6cd53cc-906b-4c05-a715-f265380a9562)

### 组装节点 seeds 信息

以下是格式说明：

```
<Peer_ID>:
  addresses:
  # with DNS
  - "/dns4/<DNS_Name>/tcp/<Port_Number>/noise-ik/<Public_Key>/handshake/0"
  role: Upstream
<Peer_ID>:
  addresses:
  # with IP
  - "/ip4/<IP_Address>/tcp/<Port_Number>/noise-ik/<Public_Key>/handshake/0"
  role: Upstream
```

#### 方式一：IP 地址

假设：

* 您的 IP 为 5.161.43.102；
* 公钥和 Peer ID 是 1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e；

那么组装结果为：

```
1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e:
  addresses:
  - "/ip4/5.161.43.102/tcp/6180/noise-ik/1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e/handshake/0"
  role: "Upstream"
```

#### 方式二：域名

若您有域名，且域名解析到了节点服务器，则可以使用该方式。

假设：

* 您的节点域名为 node1.devnet.aptos.movemove.org（<mark style="color:blue;">该节点域名仅作演示，实际不存在</mark>）
* 公钥和 Peer ID 是 1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e；

那么组装结果为：

```
1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e:
  addresses:
  - "/dns4/node1.devnet.aptos.movemove.org/tcp/6180/noise-ik/1741d4ef7b057349f8162b50b84b5959c872c927fba037a502aeca0da4b60d3e/handshake/0"
  role: Upstream
```

{% hint style="info" %}
您也可以向社区提交 issue，以共享您的节点，<mark style="color:red;">提交前请确保您的节点可用，并长期维护</mark>。

地址：<https://github.com/jiangydev/aptos-node/issues>
{% endhint %}


# 如何运行多个节点

如果您拥有一台高性能服务器，想运行多个节点，那么您可以参考这个教程。

{% hint style="info" %}
注意：

* 官方建议，单个全节点的最低配置为 2 核 8GB 内存；
* 使用本教程前，请确保已阅读并了解如何运行节点（Docker 方式）；[教程地址](/jiao-cheng/kai-fa-wang-devnet/yun-xing-quan-jie-dian-she-qu-jiao-cheng)
* 若您的服务器性能较低，运行多个节点，可能会导致宕机、无法连接等情况！
  {% endhint %}

## 1. 创建新的节点目录

#### Windows 用户

在 C 盘或 D 盘下新建文件夹 `aptos-node-1`，注意不是之前节点的目录。

#### Mac/Linux 用户

前面的教程中，默认创建了名为 `aptos-node` 的目录，为了运行新的节点，需要创建新的目录：

```shell
# 创建 aptos-node 目录并进入该目录，之后的操作都会在该目录下进行
mkdir -p ~/aptos-node-1 && cd ~/aptos-node-1
```

## 2. 下载 Aptos 节点运行所需文件

在新的目录（`aptos-node-1`）下重新下载节点所需文件，您也可以从之前的目录中拷贝。

## 3. 修改配置

### 3.1 修改新的静态身份

新的节点需要生成新的静态身份，您可以参照社区教程的命令生成并修改 `public_full`\_node`.yaml` 文件。

```yaml
base:
    # This is the location Aptos will store its database. It is backed by a dedicated docker volume
    # for persistence.
    data_dir: "/opt/aptos/data"
    role: "full_node"
    waypoint:
        # This is a checkpoint into the blockchain for added security.
        from_file: "/opt/aptos/etc/waypoint.txt"

execution:
    # Path to a genesis transaction. Note, this must be paired with a waypoint. If you update your
    # waypoint without a corresponding genesis, the file location should be an empty path.
    genesis_file_location: "/opt/aptos/etc/genesis.blob"

full_node_networks:
    - network_id: "public"
      discovery_method: "onchain"
      identity:
        type: "from_config"
        key: "这里换成自己生成的私钥"
        peer_id: "这里换成自己生成的PeerID"
      # The network must have a listen address to specify protocols. This runs it locally to
      # prevent remote, incoming connections.
      listen_address: "/ip4/0.0.0.0/tcp/6180"
      # Define the upstream peers to connect to
      seeds:
        bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
            addresses:
            - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/ln-handshake/0"
            role: "Upstream"
        7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61:
            addresses:
            - "/dns4/pfn1.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61/ln-handshake/0"
            role: "Upstream"
        f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b:
            addresses:
            - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/ln-handshake/0"
            role: "Upstream"

api:
    # This specifies your REST API endpoint. Intentionally on public so that Docker can export it.
    address: 0.0.0.0:8080

```

修改后的结果如下：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2F8Kg1Q1Cxl4PQCnB7gRYZ%2Fimage.png?alt=media\&token=9fd1e526-c7af-4858-902c-fb687ab9e23c)

### 3.2 修改 docker-compose 编排文件

这是最重要的一步，打开 `docker-compose.yaml` 编排文件，并修改端口配置。

找到 ports 属性，并修改冒号左侧的端口，这里我将 8080 修改为 8081，将 9101 修改为 9102，将 6180 修改为 6181。

{% hint style="info" %}
注意：

* 修改后的端口一定要是可用的，没有其他程序占用，否则节点将无法运行；
* "6180:6180" 默认不存在，可以不修改。
* 注意本步骤中，将 9101 端口修改为 9102，最后检查节点时，命令也有所修改。
  {% endhint %}

修改结果如下图所示：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FhTt33E5T4aJwhHEdwcYN%2Fimage.png?alt=media\&token=4f8b89e2-fc07-4435-9b49-f725ef0d4d7f)

## 4. 运行新的全节点

在 `aptos-node-1` 目录下，输入如下命令运行节点：

```
# 更新镜像
sudo docker-compose pull

# 运行节点
sudo docker-compose up -d
```

查看节点运行状态：

```
sudo docker ps -a 
```

![node container running](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FcvDy3C1Hl5hXusgVjTeC%2Flinux-node-pod-running.png?alt=media\&token=5e2942e9-9dad-4f4e-ac70-4126aedd9d91)

至此，新的节点已经启动完成。

## 5. 检查新节点

{% hint style="info" %}
注：

* 确保节点是运行中，否则执行下面的命令会出错！
* <mark style="color:orange;">新节点的端口有变化，在第三步中，将 9101 修改为 9102，因此命令也有所修改</mark>。
  {% endhint %}

#### Windows 用户

在浏览器中访问：[http://127.0.0.1:9102/metrics](http://127.0.0.1:9101/metrics)

#### MacOS/Linux 用户

执行如下命令查看同步状态：

```
curl 127.0.0.1:9102/metrics 2> /dev/null | grep aptos_state_sync_version | grep type
```


# 如何修改节点配置文件

{% hint style="info" %}
本篇提供给源码方式运行节点的社区用户，作为学习交流。
{% endhint %}

编辑 `public`\_`full_node.yaml`，配置文件，可修改以下内容：

* `base.data_dir`: 数据存储目录；
* `full_node_networks[0].listen_address`: peer 监听地址及端口；
* `debug_interface.admission_control_node_debug_port`: DEBUG 端口；
* `debug_interface.metrics_server_port`: 指标获取端口；
* `debug_interface.public_metrics_server_port`: 指标获取端口；
* `api.address`: API 接口地址及端口；

需要修改的内容如下图标注所示：

![](https://837065856-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEVfhRe3oc2wVpOxYrh1V%2Fuploads%2FHjNfSrgYWaaNHBUgkt5G%2Fimage.png?alt=media\&token=345037c1-b824-4852-968f-10895d207a70)

配置文件内容：

```yaml
base:
    # This is the location Aptos will store its database. It is backed by a dedicated docker volume
    # for persistence.
    data_dir: "/opt/aptos/data"
    role: "full_node"
    waypoint:
        # This is a checkpoint into the blockchain for added security.
        from_file: "/opt/aptos/etc/waypoint.txt"

execution:
    # Path to a genesis transaction. Note, this must be paired with a waypoint. If you update your
    # waypoint without a corresponding genesis, the file location should be an empty path.
    genesis_file_location: "/opt/aptos/etc/genesis.blob"

full_node_networks:
    - network_id: "public"
      discovery_method: "onchain"
      # The network must have a listen address to specify protocols. This runs it locally to
      # prevent remote, incoming connections.
      listen_address: "/ip4/0.0.0.0/tcp/6180"
      # Define the upstream peers to connect to
      seeds:
        bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a:
            addresses:
            - "/dns4/pfn0.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/bb14af025d226288a3488b4433cf5cb54d6a710365a2d95ac6ffbd9b9198a86a/ln-handshake/0"
            role: "Upstream"
        7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61:
            addresses:
            - "/dns4/pfn1.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/7fe8523388084607cdf78ff40e3e717652173b436ae1809df4a5fcfc67f8fc61/ln-handshake/0"
            role: "Upstream"
        f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b:
            addresses:
            - "/dns4/pfn2.node.devnet.aptoslabs.com/tcp/6182/ln-noise-ik/f6b135a59591677afc98168791551a0a476222516fdc55869d2b649c614d965b/ln-handshake/0"
            role: "Upstream"

debug_interface:
    admission_control_node_debug_port: 6191
    address: "0.0.0.0"
    metrics_server_port: 9101
    public_metrics_server_port: 9102

api:
    # This specifies your REST API endpoint. Intentionally on public so that Docker can export it.
    address: 0.0.0.0:8080

```


