运行 Wormhole Guardian 节点

本文档详细介绍了如何运行 Wormhole Guardian 节点,包括连接的链、所需节点的要求(如 Solana、以太坊、Terra 等)、wormchain 的配置和使用。强调了安全性,建议从源代码构建 guardiand 二进制文件,并详细说明密钥生成、部署、监控、原生代币转账(NTT)及跨链查询(CCQ)的配置步骤,同时鼓励 Guardian 节点公开 API 端点以提高协议的鲁棒性。

运行 Wormhole Guardian 节点

连接的链

除了 Wormhole 本身,你需要为你所连接的每个链运行你自己的验证节点,除了通过 Wormhole Gateway 集成的新 IBC 连接的链。请参考 常量参考 获取 Wormhole 连接的所有链。

对于任何链,不要使用第三方 RPC 服务提供商! 你会完全信任他们,他们可能会在事件是否被实际观察到这件事上对你撒谎。 Wormhole 的全部意义在于不依赖中心化节点!

我们强烈建议为测试网和主网(如果适用)运行你自己的完整节点,这样你可以测试主网完整节点的更改并获得操作经验。

Solana 节点要求

参考 Solana 文档 了解如何运行 RPC(完整)节点。Solana 的 Discord 服务器 是一个很好的资源,可以解答有关操作的问题。

#rpc-server-operators 频道对于设置 Solana RPC 节点尤其有用。

你的 Solana RPC 节点需要启用以下参数:

--enable-rpc-transaction-history
--enable-cpi-and-log-storage

--enable-rpc-transaction-history 允许通过 getConfirmedBlock API 检索历史交易,这是 Wormhole 查找交易所需的。

--enable-cpi-and-log-storage 存储关于 CPI 调用的元数据。

请注意,这些需要额外的磁盘空间!

账户索引

如果你为 Wormhole v1 使用相同的 RPC 节点,你还需要以下附加参数来加速 getProgramAccount 查询:

<!-- cspell:disable -->

[... 请参阅上面其他必需的参数 ...]

--account-index program-id
--account-index-include-key WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC   # 用于主网
--account-index-include-key 5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg  # 用于测试网

<!-- cspell:enable -->

或者,如果你想运行一个通用 RPC 节点,该节点具有所有程序(而不仅仅是 Wormhole)的索引,请忽略过滤:

--account-index program-id

在主网上,我们强烈建议将 KIN 和 token 程序列入黑名单,以加快追赶速度:

<!-- cspell:disable -->

--account-index-exclude-key kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6  # 仅限主网
--account-index-exclude-key TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA  # 仅限主网

<!-- cspell:enable -->

请注意,这些索引需要额外的磁盘空间,并且可能会减慢追赶速度。 添加这些参数后的首次启动将很慢,因为 Solana 需要重建所有索引。

Ethereum 节点要求

为了观察以太坊链上的事件,你需要访问以太坊 RPC 端点。 最常见的选择是 geth,但为了多样性,你可能想运行一些不是 geth 的东西。

使用 Alchemy、Infura 等 RPC 提供商,你信任这些运营商为你提供未被篡改的链数据,并且无法验证正确性。 因此,Wormhole 需要以太坊完整节点或轻客户端。 该节点可以在完整、快速或轻模式下运行,而不会影响桥接软件的安全性或性能。 只要该节点支持以太坊 JSON RPC API,它就与桥接兼容,因此所有主要实现都可以正常工作。

通常,完整节点比轻客户端工作得更好、更可靠,因为轻客户端容易受到 DoS 攻击,因为只有极少数节点支持轻客户端协议。

运行完整节点通常需要 ~500G 的 SSD 存储、8G 的 RAM 和 4-8 个 CPU 线程(取决于时钟频率)。 轻客户端的硬件要求要低得多。

Terra

Terra 有时也称为 Terra 2,但正确的名称只是“Terra”。 先前版本已重命名为“Terra Classic”。

参考 Terra 文档 了解如何运行完整节点。

Terra Classic

参考 Terra Classic 文档 了解如何运行完整节点。

Wormchain

所有守护者必须为 wormchain 运行验证器,这是 Wormhole Gateway 的代号。

前提条件
  • 确保你已安装 Go >= 1.23.3。
构建 wormchaind 二进制文件

没有可用的预构建二进制文件。 你需要检出 repo 并从源代码构建 wormchaind 二进制文件:

首先,检出你要部署的 Wormhole repo 的版本:

git clone https://github.com/wormhole-foundation/wormhole && cd wormchain

然后,以非特权构建用户身份编译发布二进制文件:

make build/wormchaind

你将在 wormchain/build/wormchaind 中找到 wormchaind 二进制文件。

初始化环境

使用必要的配置初始化 Wormchain 环境。

/path/to/wormchain/bin/wormchaind init &lt;你的节点名称> --chain-id wormchain --home /path/to/wormchain/directory

此命令将生成私有验证器密钥。 该密钥应该备份。 此密钥在 config.toml 文件中的 priv_validator_key_file 字段下设置。

生成配置文件

guardian 节点的 --wormchainURL 参数应指向 &lt;validator address>:9090,这是 app.toml 中的 grpc 端口。

示例端口设置:

<!-- cspell:disable -->

config.toml:

[rpc]
laddr = "tcp://0.0.0.0:26657"
grpc_laddr = ""
pprof_laddr = "localhost:6060"

[p2p]
laddr = "tcp://0.0.0.0:26656"
external_address = ""

app.toml:

[grpc]
address = "0.0.0.0:9090"

[grpc-web]
address = "0.0.0.0:9091"

<!-- cspell:enable -->

对于签名,请考虑设置远程阈值签名器,例如 horcrux,并采用在 wormchain 验证器前使用哨兵节点的哨兵节点架构。

检索 Genesis 文件

从 wormhole 存储库下载 genesis 文件。

wget -O /path/to/wormchain/config/genesis.json https://raw.githubusercontent.com/wormhole-foundation/wormhole/main/wormchain/&lt;network>/genesis.json
生成 wormchain 密钥

要生成 wormchain 密钥,请运行以下命令:

/path/to/wormchain/bin/wormchaind keys add &lt;key-name> --keyring-backend file --home /path/to/wormchain/directory

此命令将创建一个 &lt;key-name>.info 文件。 要提取密钥,请运行以下命令:

wormchaind keys export &lt;key_name> --home /path/to/wormchain/directory --keyring-dir . --keyring-backend file

此命令将要求为其创建一个密码。 以下 guardiand 标志将使用 wormchain.keypassphrase

  • --accountantKeyPath
  • --accountantKeyPassPhrase

注意 这些步骤可以直接在 guardiand 节点上执行,前提是之前已经构建了 wormchaind 二进制文件。

Wormchain 实用命令

检查最新的守护者集合:

<!-- cspell:disable -->

$ wormchaind query wormhole latest-guardian-set-index
latestGuardianSetIndex: 4

<!-- cspell:enable -->

升级守护者集合(使用有效的治理 vaa):

<!-- cspell:disable -->

wormchaind tx wormhole execute-governance-vaa &lt;以十六进制格式表示的 guardian_set_upgrade_VAA>

<!-- cspell:enable -->

查看验证器信息:

<!-- cspell:disable -->

$ wormchaind q staking validators
... snipped ...
- commission:
    commission_rates:
      max_change_rate: "0.020000000000000000"
      max_rate: "0.200000000000000000"
      rate: "0.000000000000000000"
    update_time: "2024-04-16T19:13:45.210176030Z"
  consensus_pubkey:
    '@type': /cosmos.crypto.ed25519.PubKey
    key: T+hsVX52EarrsL+mOwv3mL0byWa2EctsG6XmikUMFiQ=
  delegator_shares: "0.000000000000000000"
  description:
    details: ""
    identity: 11A4103C4BCBD2B4
    moniker: RockawayX
    security_contact: ""
    website: https://rockawayx.com/infrastructure
  jailed: false
  min_self_delegation: "0"
  operator_address: wormholevaloper1thl5syhmscgnj7whdyrydw3w6vy80044278fxp
  status: BOND_STATUS_BONDED
  tokens: "0"
  unbonding_height: "0"
  unbonding_time: "1970-01-01T00:00:00Z"

<!-- cspell:enable -->

EVM 节点要求

一些非以太坊 EVM 兼容的区块链需要以存档模式运行,以便 查询 能够正常运行。 默认情况下,在 geth 中,历史状态仅在内存中保留 128 个区块。 128 块之后,较旧的状态将被垃圾回收。 这些链中的许多都是 geth 的分支,它们保持了这种历史限制。

  • Arbitrum
  • Base
  • Optimism

较新的执行客户端(例如 reth)没有这种限制,一旦稳定,值得研究。

此外,如果由于硬分叉或某些不可预见的情况,网络未能就 EVM 兼容链达成共识,则可能需要临时为这些链运行存档节点,以确保可以重新观察到这些交易。

Cosmos / IBC 连接的节点

所有现代 Cosmos 集成都是通过 Wormhole 观察 Gateway(wormchain)上的 IBC 交易来实现的。 Guardian 节点运营商不需要为这些网络运行完整节点。 对于在此功能之前添加的基于 Cosmos 的链,仍然需要完整节点。

以下基于 Cosmos 的节点是在 Gateway 之前添加的,守护者需要运行完整节点:

  • Injective
  • Terra
  • Terra Classic
  • XPLA

注意:所有守护者必须为 wormchain 运行验证器。

构建 guardiand

出于安全原因,我们不提供预构建的二进制文件。 你需要检出 repo 并从源代码构建 guardiand 二进制文件。 Git 存储库比发布二进制文件更难篡改。

要构建 Wormhole 节点,你需要 Go >= 1.23.3

首先,检出你要部署的 Wormhole repo 的版本:

git clone https://github.com/wormhole-foundation/wormhole && cd wormhole

然后,以非特权构建用户身份编译发布二进制文件:

make node

你最终会在 build/ 中得到一个 guardiand 二进制文件。

考虑这些建议,而不是盲目遵循的教程。 你需要将其与你现有的构建管道集成。 如果你需要 Dockerfile 示例,可以查看我们的开发网络部署。

如果你想在本地编译和部署,可以运行 sudo make install 将二进制文件安装到 /usr/local/bin。

如果你使用自定义管道进行部署,则需要在二进制文件上设置 CAP_IPC_LOCK 功能(例如,执行等效于 sudo setcap cap_ipc_lock=+ep 的操作),以允许它锁定其内存页,以防止它们被换出。 请参阅下文,了解原因 - 这是一种通用的深度防御缓解措施,可确保进程内存永远不会交换到磁盘。 如果此额外功能代表运营或合规性问题,请创建一个 GitHub issue。

密钥生成

要生成 guardian 密钥,请先安装 guardiand。 如果你在单独的机器上生成密钥,你可能只想编译 guardiand 而不安装它:

make node
sudo setcap cap_ipc_lock=+ep ./build/bin/guardiand

否则,请使用你使用上述常规说明编译的相同 guardiand 二进制文件。

使用 keygen 子命令生成一个新密钥:

guardiand keygen --desc "Testnet key foo" /path/to/your.key

密钥文件包括一个人类可读的部分,其中包括公钥哈希和描述。

部署

我们强烈建议为 Wormhole 服务使用单独的用户和 systemd 服务。

请参阅单独的 wormhole-networks 存储库,以获取有关如何为特定网络设置 guardiand 单元的示例。

你需要在防火墙中打开端口 8999/udp 用于 P2P 网络,8996/udp 用于 跨链查询。 如果你不运行公共 RPC,则无需在外部公开其他任何内容。

journalctl 可以使用 -a 标志显示 guardiand 的彩色输出以进行二进制输出,即:journalctl -a -f -u guardiand

Kubernetes

完全支持 Kubernetes 部署。

请参阅 devnet/,以获取示例 k8s 部署,作为你自己的生产部署的起点。 你必须构建自己的容器。 除非你已经在生产中运行 Kubernetes,否则我们强烈建议在专用实例上进行传统部署 - 这样更容易理解和排除故障。

在 kubernetes 中运行或在任何类型的 NAT 之后运行时,将 --gossipAdvertiseAddress=external.ip.address 传递给 guardiand 节点进程,以确保在 p2p 中公布外部地址。 如果不这样做,重新观察请求和 CCQ 将无法按预期运行。

监控

Wormhole 公开了一个状态服务器,用于就绪状态和指标。 默认情况下,它在 localhost 上的端口 6060 上侦听。 你可以使用命令行参数公开它:--statusAddr=[::]:6060

注意: 不建议解析日志输出进行监控。 日志输出供人们使用,不被视为稳定的 API。 日志消息可能会在没有通知的情况下被添加、修改或删除。 使用指标 :-)

/readyz

一旦 Wormhole 节点准备好为请求提供服务,此端点将返回 200 OK 状态代码。 只要节点成功连接到所有链并开始处理请求,该节点就被认为已准备就绪。

仅用于启动信号 - 它不会告诉你在稍后的某个时间点是否 停止 处理请求。 一旦它为真,它就一直为真! 使用指标来找出答案。

/metrics

此端点提供 Prometheus 指标,用于警报和自省。 我们建议使用 Prometheus 和 Alertmanager,但任何可以使用标准化 Prometheus 公开格式提取指标的监控工具都可以工作。

一旦我们获得了更多关于 Wormhole 的运营经验,此处将记录关于基于症状的适当警报的具体建议。

请参阅 Wormhole.json,以获取 Grafana 仪表板示例。

Wormhole 仪表板

有一个 仪表板,它显示了网络的整体健康状况,并具有关于各个守护者的指标。

注意: 不建议解析日志输出进行监控。 日志输出供人们使用,不被视为稳定的 API。 日志消息可能会在没有通知的情况下被添加、修改或删除。 使用指标 :-)

Wormhole Fly 健康检查

wormhole-dashboard 存储库中,有一个小的 健康检查应用程序,它验证守护者是否正在 gossiping 出心跳信号,是否正在提交链观察,并且是否有一个可用的工作心跳信号 API。 这是验证特定守护者是否按预期运行的非常好的方法。

你可以克隆 repo 并针对 MCF Guardian 运行检查:

git clone https://github.com/wormhole-foundation/wormhole-dashboard
cd wormhole-dashboard/fly/cmd/healthcheck

## 运行 fly
$ go run main.go --pubKey 0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811 --url https://wormhole-v2-mainnet-api.mcf.rocks
✅ 收到 guardian 心跳信号 {12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU: [/ip4/185.188.42.109/udp/8999/quic-v1]}
✅ 收到 44 次观察结果
✅ /v1/heartbeats

如果未公开 guardian 公共 RPC,则可以省略 --url 标志:

$ go run main.go --pubKey 0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811
✅ 收到 guardian 心跳信号 {12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU: [/ip4/185.188.42.109/udp/8999/quic-v1]}
✅ 收到 41 次观察结果
ℹ️  --url 未定义,跳过 Web 检查

引导节点和网络默认为 mainnet,这些值可以在 网络常量 中找到。

它也可以用于测试特定的引导节点:

$ go run main.go --pubKey 0xDA798F6896A3331F64b48c12D1D57Fd9cbe70811 --bootstrap /dns4/wormhole.mcf.rocks/udp/8999/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU
✅ 收到 guardian 心跳信号 {12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU: [/ip4/185.188.42.109/udp/8999/quic-v1]}
✅ 收到 44 次观察结果
ℹ️  --url 未定义,跳过 Web 检查

原生 Token 转移

NTT 是 wormhole 的一项令人兴奋的功能,它构建在核心桥之上,以允许铸造/销毁风格转移。 确保它正确运行需要将其与 NTT Accountant 集成。 要启用此功能,请创建一个新的 wormchain 密钥。 不要重用现有的全局 accountant 密钥,并添加以下参数:

<!-- cspell:disable -->

## 你可能已经拥有这些。
--wormchainURL URL_TO_YOUR_WORMCHAIN_NODE
--accountantWS HTTP_URL_OF_YOUR_WORMCHAIN_NODE

## 这是主网合约。
--accountantNttContract wormhole1mc23vtzxh46e63vq22e8cnv23an06akvkqws04kghkrxrauzpgwq2hmwm7

--accountantNttKeyPath PATH_TO_YOUR_NTT_ACCOUNTANT_KEY_FILE
--accountantNttKeyPassPhrase YOUR_NTT_ACCOUNTANT_KEY_PASS_PHRASE

<!-- cspell:enable -->

请记住允许将新的 NTT Accountant 密钥用于 Wormchain! 有关如何执行此操作的说明,请与 Wormhole Foundation 的某人联系。

跨链查询

CCQ 也称为 Wormhole 查询,是一种允许以跨链方式提取 attestation 的功能。 要运行 ccq,需要在 guardian 节点上启用一些附加标志:

<!-- cspell:disable -->

--ccqEnabled=true \
--ccqAllowedPeers="[ALLOWED,PEERS,GO,HERE]" \
--ccqAllowedRequesters="[ALLOWED,REQUESTORS,GO,HERE" \

<!-- cspell:enable -->

要测试查询功能,请按照 node/hack/query/ccqlistener/ccqlistener.go 中的说明进行操作。

运行公共 API 端点

Wormhole v2 不再使用 Solana 作为数据可用性层(请参阅 设计文档)。 而是依赖于 Guardian 节点公开一个 API,Web 钱包和其他客户端可以使用该 API 来检索给定交易的已签名 VAA 消息。

强烈建议 Guardian 节点公开一个公共 API 端点以提高协议的稳健性。

guardiand 附带一个内置的 REST 和 grpc-web 服务器,可以使用 --publicWeb 标志启用:

--publicWeb=[::]:443

为了与 Web 钱包一起使用,需要支持 TLS。 guardiand 具有内置的 Let's Encrypt 支持:

--tlsHostname=wormhole-v2-mainnet-api.example.com
--tlsProdEnv=true

或者,你可以使用像 CloudFlare 这样的托管反向代理来终止 TLS。

在签名节点上公开 publicWeb 端口是安全的。 为了更好地抵御拒绝服务攻击,未来的 guardiand 版本将包括仅侦听模式,这样可以在负载均衡器后面运行多个没有 guardian 密钥的 guardiand 实例。

启用遥测

(可选)guardian 可以将遥测数据发送到 Grafana Cloud Logs,也称为 "loki"。 要启用此功能,请添加以下标志:

--telemetryLokiURL=$PER_GUARDIAN_LOKI_URL_WITH_TOKEN

新的 guardian 应该与 Wormhole Foundation 联系以获取 Loki url。

绑定到特权端口

如果你想将 --publicWeb 绑定到 <1024 的端口,你需要分配 CAP_NET_BIND_SERVICE 功能。 这可以通过将该功能添加到二进制文件(如在非 systemd 环境中)来完成:

 sudo setcap cap_net_bind_service=+ep guardiand

...或者通过扩展 guardiand.service 中的功能集来完成:

AmbientCapabilities=CAP_IPC_LOCK CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_BIND_SERVICE

密钥管理

你必须管理以下密钥:

  • guardian 密钥,它是桥共识密钥。 此密钥非常重要 - 你的节点使用它来验证 VAA 消息。 公钥的哈希存储在所有连接链上的 guardian 集合中。 它不会累积奖励。 它是保护 Wormhole 网络的 multisig 机制的份额。 如果大多数 guardian 同意签名并发布新的 guardian 集合,则可以替换 guardian 集合。

  • 节点密钥,它在 gossip 网络上识别它,类似于 Solana 的节点身份或 Tendermint 节点密钥。 它被对等网络用于路由和传输层加密。 攻击者可能会使用它来审查你在网络上的消息。 除此之外,它不是很重要,可以轮换。 如果你指定的路径中不存在节点密钥,该节点将自动创建节点密钥。 虽然节点密钥可以被替换,但我们建议使用持久节点密钥。 这将使你更容易在监控数据中识别你的节点,并提高 p2p 连接性。

对于生产环境,我们强烈建议你加密你的磁盘,并且/或者注意不要让热 guardian 密钥接触磁盘。 实现此目的的一种方法是将密钥存储在内存 ramfs 上,该 ramfs 无法换出,并在每次节点重启时从冷存储或 HSM/vault 恢复它。 你可能想要完全禁用交换。 这些都不是 Wormhole 特有的 - 这适用于任何热密钥。

我们的节点软件会格外小心地使用 mlock(2) 锁定内存,以防止密钥被交换到磁盘,这就是为什么它需要额外的功能。 是的,其他链可能也想这样做 :-)

将密钥存储在 HSM 上或使用远程签名者只能部分缓解服务器被入侵的风险 - 这意味着密钥不会被盗,但攻击者仍然可能导致 HSM 签署恶意负载。 Wormhole 的未来迭代可能会包括对远程签名的支持。

引导对等点

支持的引导对等点的列表在 node/pkg/p2p/network_consts.go 中定义。 该文件还提供了用于根据环境(主网或测试网)获取网络参数(网络 ID 和引导对等点)的 golang 函数。

常见的 Wormhole 应用程序(guardiand、spy 和查询代理服务器)使用这些函数,因此无需在其配置中指定实际的引导参数。 强烈建议任何新应用程序的开发人员也这样做,并且不要扩散可能随时间变化的引导对等点列表。

运行 Guardian Spy

间谍连接到 wormhole guardian 对等网络并侦听新的 VAA。 它通过套接字和 websocket 发布这些 VAA,应用程序可以订阅这些 VAA。 如果你想运行从源代码构建的间谍,请在构建 guardian 映像后将 ghcr.io/wormhole-foundation/guardiand:latest 更改为 guardian

针对测试网 wormhole guardian 启动间谍:

<!-- cspell:disable -->

docker run \
    --pull=always \
    --platform=linux/amd64 \
    -p 7073:7073 \
    --entrypoint /guardiand \
    ghcr.io/wormhole-foundation/guardiand:latest \
    spy --nodeKey /node.key --spyRPC "[::]:7073" --env testnet

<!-- cspell:enable -->

要对 mainnet 运行间谍:

<!-- cspell:disable -->

docker run \
    --pull=always \
    --platform=linux/amd64 \
    -p 7073:7073 \
    --entrypoint /guardiand \
    ghcr.io/wormhole-foundation/guardiand:latest \
    spy --nodeKey /node.key --spyRPC "[::]:7073" --env mainnet

<!-- cspell:enable -->

Guardian 配置

支持配置文件、环境变量和标志。

配置文件

位置/命名:默认情况下,配置文件应位于 node/config 目录中。 配置文件的标准名称是 guardiand.yaml。 目前尚不支持自定义目录或文件名。

格式:我们支持 Viper 支持的任何格式。 但通常首选 YAML 格式。

示例

<!-- cspell:disable -->

ethRPC: "ws://eth-devnet:8545"
ethContract: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
solanaRPC: "http://solana-devnet:8899"
solanaContract: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"

<!-- cspell:enable -->

环境变量

前缀:与 Guardian 节点相关的所有环境变量应以 GUARDIAND_ 为前缀。

用法:环境变量可用于覆盖配置文件中的设置。 尤其适用于不应存储在配置文件中的敏感数据(如 API 密钥)。

示例

export GUARDIAND_ETHRPC=ws://eth-devnet:8545

命令行标志

用法:标志提供最高优先级,可用于临时覆盖或用于频繁更改的设置。

示例

./guardiand node --ethRPC=ws://eth-devnet:8545

优先级顺序

配置设置按以下优先级顺序应用:

  1. 命令行标志:最高优先级,覆盖任何其他设置。
  2. 环境变量:覆盖配置文件设置,但可以被标志覆盖。
  3. 配置文件:最低优先级。
  • 原文链接: github.com/wormhole-foun...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
wormhole-foundation
wormhole-foundation
江湖只有他的大名,没有他的介绍。