NEAR (Blockchain Platform)

Table of Contents

1. 简介

NEAR 是一个采用 Sharding 方案的 PoS 区块链。

1.1. 原生币单位

NEAR 中的原生币的最小单位被称为 yoctoNEAR,它和 NEAR 的关系为: \[\begin{aligned} 1 \;\text{NEAR} &= 1000000000000000000000000 \;\text{yoctoNEAR} \\ &= 10^{24} \;\text{yoctoNEAR} \end{aligned}\]

2. 帐户模型

NEAR 中有两个类型的 Accounts

  1. Named Account,它是 human readable 的形式,比如主网上的帐户 alice.near,测试网上的帐户 example.testnet
  2. Implict Account,它是 ED25519 公钥的 16 进制编码,比如 98793cd91a3f870fb126f66285808c7e094afcfc4eda8a970f6648cdf0dbd6de

通过 near create-account 可以创建 Named accounts。但如果你在主网上操作,则需要提前准备另一个有 NEAR 币的帐户来创建这个新的 Named Account。我们可通过 near generate-key 创建 Implict Account,它不需要消耗 NEAR 币。

每个帐户可以绑定多个 Access Key,通过 RPC view_access_key_list 查看帐户所绑定的 Access Key,具体可参考节 4.2

3. 交易

3.1. 交易结构

每个 Tx 由下面六个部分组成:

  1. signerId:签名帐户。
  2. signerPublicKey:签名所使用的公钥。一个帐户可以绑定多个公钥,所以需要指定使用帐户的哪一个对应私钥进行签名。
  3. nonceForPublicKey:公钥的 nonce 值,一个帐户可以绑定多个公钥,nonce 值和公钥绑定,而不是和帐户绑定。通过 RPC view_access_key_list 可以查询公钥 nonce 值。
  4. receiverId:接收帐户。
  5. blockHash:每个 Tx 都需要引用一个近期的区块 Hash,以验证这个 Tx 是最近创建的。
  6. actions:每个 Tx 可以包含一个或者多个 Action(s),参考节 3.1.1

参考:
https://docs.near.org/integrator/create-transactions
https://github.com/near/near-api-js/blob/f28796267327fc6905a8c6a7051ff37aaa7bbd06/packages/transactions/src/schema.ts#L224

3.1.1. Actions

Tx 的具体动作由 Action 指定,目前有 9 个 Actions,它们分别为 CreateAccount/DeployContract/FunctionCall/Transfer/Stake/AddKey/DeleteKey/DeleteAccount/Delegate。比如,对于普通的转帐来说使用的 Action 是 Transfer。

3.2. Receipts

NEAR 支持 Sharding,这给系统带来了一些复杂性。比如 alice.near 给 bob.near 转账时,他们的帐户信息可能不在一个 Sharding 中,这就涉及到跨 Sharding 之间的通信。

NEAR 引入了 Receipts 的概念,我们 可以简单地认为 Receipts 是在不同 Sharding 之间传递信息的内部交易。

You can think of the Receipt as an internal transaction that exists to pass information across shards.

1 是 Receipt 示意图。

参考:https://docs.near.org/concepts/data-flow/token-transfer-flow

3.3. 手续费

在每笔交易中,NEAR 网络都会收取少量的 Gas 费用。NEAR 的 Gas 机制除了用于防止攻击者发送无用交易之外,还有其它特点:

  1. 智能合约开发者会获得用户调用合约时所消耗 Gas 的 30%,从而激励开发者;
  2. 每个交易限制 300 Tgas(大约 300ms 的计算时间)。

手续费计算公式:

Fee = Gas Units * Gas Price

其中 Gas Units 是 Gas 数量,而 Gas Price 就是每个 Gas Unit 的 yoctoNEAR 价格。

NEAR Tx 的手续费,并没有体现在 Tx 中,而是由节点直接扣除,所以不需要用户指定手续费。目前,用户也无法指定额外小费来加速 Tx 的上链过程。

参考:https://docs.near.org/concepts/protocol/gas

3.3.1. Gas Units

大部分 NEAR 中的 Action 会消耗固定数量的 Gas Units,比如 Action CreateAccount/Transfer 等都是固定的 Gas Units。 具体消耗的 Gas Units 值是在 NEAR 节点的配置文件中设置的,参考:https://github.com/near/nearcore/blob/master/core/parameters/res/runtime_configs/parameters.yaml

当然对于 Action DeployContract 来说,它的 Gas Units 会和合约的大小有关系;对于 Action FunctionCall 来说,它的 Gas Units 和具体调用的合约有关系。要估计 Action FunctionCall 的 Gas Units,可以参考:https://docs.near.org/concepts/protocol/gas#estimating-costs-for-a-call

Gas Units 一般使用 Tgas 表示,它是换算关系为:1 Tgas = 1000000000000 Gas Units = \(10^{12}\) Gas Units

3.3.2. Gas Price

Gas Price 就是每个 Gas Unit 的 yoctoNEAR 价格。通过 RPC gas-price 可以查询指定区块,或者最新区块的 Gas Price。目前,每个 Gas Unit 消耗 100000000 yoctoNEAR。

4. 帐户基本操作(命令行演示)

下面演示一下如何通过 NEAR CLI 进行帐户的基本操作,如创建、转账、查看余额等。

下面是安装 NEAR CLI 的命令:

$ npm install -g near-cli                                               # 安装 NEAR CLI

4.1. 创建帐户

使用 near create-account <your-account-name> 可以创建你指定的帐户。比如在测试网中创建帐户 my-first.testnet(测试网中的帐户一般以 .testnet 结尾,主网中的帐户一般以 .near 结尾):

$ near create-account my-first.testnet --useFaucet --networkId testnet  # 创建测试网的帐户 my-first.testnet,并从内置 faucet 中领取测试币
Storing credentials for account: my-first.testnet (network: testnet)
Saving key to '~/.near-credentials/testnet/my-first.testnet.json'

我们可以看看密钥文件的具体内容:

$ cat ~/.near-credentials/testnet/my-first.testnet.json                 # 查看保存的密钥(ed25519 公钥和私钥采用 base58 编码)
{"account_id":"my-first.testnet","public_key":"ed25519:7fkqm12NvLXSJzF4nxmyPYTmstQ6xNuZ2Pi5UpRAooS1","private_key":"ed25519:4ZHyGAY8XEr3Z3JxBTZsp17b8HHhjnC5S3T82t3Kgr2da3UCg58CsovZk6g5iiiVp2gyP8xDs9WX5TFkf2kJtQfb"}

可见,密钥文件是个 JSON 文件,保存了三个信息:account_id/public_key/private_key。其中 public_key 是 ed25519 公钥(32 字节)的 base58 编码;而 private_key 是 ed25519 私钥(32 字节)和公钥(32 字节)concatenate 后组成的数据的 base58 编码。

4.2. 查看帐户绑定的公钥

在 NEAR 中一个帐户可以绑定多个 Key,使用 near list-keys <your-account-name> 可以查看帐户所绑定的 Key(即绑定哪个 ed25519 公钥),比如:

$ near list-keys my-first.testnet --networkId testnet
Keys for account my-first.testnet
[
  {
    access_key: { nonce: <BN: 91d00e4aacc0>, permission: 'FullAccess' },
    public_key: 'ed25519:7fkqm12NvLXSJzF4nxmyPYTmstQ6xNuZ2Pi5UpRAooS1'
  }
]

我们也可以使用 RPC view_access_key_list 查看帐户所绑定的 Key,比如:

$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0",
  "id": "dontcare",
  "method": "query",
  "params": {
    "request_type": "view_access_key_list",
    "finality": "final",
    "account_id": "my-first.testnet"
  }
}' https://rpc.testnet.near.org                                 # 使用 RPC view_access_key_list 查看帐户所绑定的 Key
{
    "jsonrpc": "2.0",
    "result": {
        "block_hash": "EXTyRjyPrKH1rgPUk3G9pYHiAN1iaGnrZHwrxtt4vX2Z",
        "block_height": 160327217,
        "keys": [
            {
                "access_key": {
                    "nonce": 160322779000000,
                    "permission": "FullAccess"
                },
                "public_key": "ed25519:7fkqm12NvLXSJzF4nxmyPYTmstQ6xNuZ2Pi5UpRAooS1"
            }
        ]
    },
    "id": "dontcare"
}

4.3. 测试转帐

在测试转账前,我们先创建另一个帐户,以作为接收帐户。这个接收帐户我们将使用 Implicit 帐户(帐户名就是 ed25519 公钥对应的 hex string):

$ near generate-key --saveImplicit --networkId testnet                  # 创建 Implicit 帐户(帐户名就是 ed25519 公钥对应的 hex string)
Seed phrase: bring crime prevent mention guess capable image box flight ill elite judge
Key pair: {"publicKey":"ed25519:DPbWgbUVxRogEekpPkEJLmkjjgQ1KZYMFP4H4GU1BfCZ","secretKey":"ed25519:5RhWFMGJnxupgWZTXqPPt9wJmwpJ5FMUWPecR2ePYJg4r74xjNXVFr4YjAAtNkoD1CrMmztSW81t1d23nnoz2zVK"}
Implicit account: b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6
Storing credentials for account: b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6 (network: testnet)
Saving key to '~/.near-credentials/testnet/b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6.json'
$ cat ~/.near-credentials/testnet/b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6.json
{"account_id":"b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6","public_key":"ed25519:DPbWgbUVxRogEekpPkEJLmkjjgQ1KZYMFP4H4GU1BfCZ","private_key":"ed25519:5RhWFMGJnxupgWZTXqPPt9wJmwpJ5FMUWPecR2ePYJg4r74xjNXVFr4YjAAtNkoD1CrMmztSW81t1d23nnoz2zVK"}

使用 near send-near 可以往另一个地址转账,如从 my-first.testnet 往 b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6 转移 1.234 NEAR:

$ near send-near my-first.testnet b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6 1.234
Sending 1.234 NEAR to b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6 from my-first.testnet
Transaction Id H1xq6hTFdCKYjkUsrH4fp9yyejdxARj2sxMoF5sQ1AFh
Open the explorer for more info: https://testnet.nearblocks.io/txns/H1xq6hTFdCKYjkUsrH4fp9yyejdxARj2sxMoF5sQ1AFh

4.4. 查看余额

使用 near state 可以查看某帐户的余额,如:

$ near state b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6
Account b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6
{
  amount: '1234000000000000000000000',
  block_hash: 'BJXB9H5oxKptvie5dZccUbcAYXjP5cGPgWF6qH2KNJK2',
  block_height: 160329036,
  code_hash: '11111111111111111111111111111111',
  locked: '0',
  storage_paid_at: 0,
  storage_usage: 182,
  formattedAmount: '1.234'
}

我们也可以使用 RPC view_account 查看帐户的余额,比如:

$ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0",
  "id": "dontcare",
  "method": "query",
  "params": {
    "request_type": "view_account",
    "finality": "final",
    "account_id": "b8160b22ba63df2115da0a7c26679069a34c66329c33ed73ec0eb7240f4706a6"
  }
}' https://rpc.testnet.near.org                                 # 使用 RPC view_account 查看帐户余额
{
    "jsonrpc": "2.0",
    "result": {
        "amount": "1234000000000000000000000",
        "block_hash": "8LxbGanAuhHv53YRvxMyQBTEEDAD7wSBpYv3Sn9zeejU",
        "block_height": 160329066,
        "code_hash": "11111111111111111111111111111111",
        "locked": "0",
        "storage_paid_at": 0,
        "storage_usage": 182
    },
    "id": "dontcare"
}

5. 智能合约

NEAR 支持使用 Javascript 和 Rust 开发智能合约,它们会被编译为 WebAssembly 代码在 NEAR 平台上执行。这里不详细介绍。

6. 参考

Author: cig01

Created: <2024-03-30 Sat>

Last updated: <2024-03-31 Sun>

Creator: Emacs 27.1 (Org mode 9.4)