星云Wiki

星云链是致力于构建可持续升级良性生态的下一代公链,具有独创的区块链价值发现体系、前瞻性的激励和共识机制、避免硬分叉的自进化能力。

星云社区秉承开放的姿态,每个人可以成为贡献者,和我们一起构建去中心化协作世界。

星云wiki是社区的一种协作工具,社区成员可以协作发布各种文档。

使用Wiki

参与贡献

什么是星云

协作的未来

星云链(Nebulas)是开源公链,星云是自治元网络(Autonomous Metanet) ,星云专注于处理复杂数据和交互、复杂的协作关系,致力于通过区块链等技术手段,实现 让每个人从去中心化协作中公平获益 的愿景。

星云借助于独有的创新技术治理链上公共资产,探索全新的去中心化协作模式,实现提供正向激励、可自进化的去中心化自治组织(Decentralized Autonomous Organization,DAO)。

4个技术特点:

  • 可量化:衡量区块链数据的尺度
  • 自进化:低成本的快速升级能力
  • 自激励:独家设计的持久正向激励
  • 链上治理:更好的DAO
自治元网络

聚焦链上复杂的数据和交互。原数据(Raw Data)如用户、智能合约等原始数据。元数据(Metadata)是用以描述数据的数据。如账户余额、地址等。超映射(Hypermapping)是指基于元数据,再抽象出一层用以描述元数据的元数据。超映射结构元数据(Hyper-mapped Structural Metadata)可以应对链上日益复杂的数据,并可以描述其复杂交互。访问 星云官网技术页 了解元数据。

举例, 星云指数(Nebulas Rank,NR) 就是一种超映射结构元数据。可以衡量区块链数据的尺度。查看 星云指数黄皮书 了解星云指数。或者访问NR页面了解更多:

星云指数(NR)

Nebulas Rank (NR) is an open sourced ranking algorithm used to measure the influence of relationships among addresses, smart contracts, and distributed applications (DApps). It helps both users utilize information among the ever-increasing amount of data on all blockchains & developers to use our search framework directly in their own applications.

https://cdn-images-1.medium.com/max/1600/1*xb-MzFJolGOy8VZvdNBIIQ.jpeg

In Nebulas, we measure value regarding:

  • Liquidity

Finance essentially is the social activities which optimize social resources via capital liquidity and promote economic development. Blockchains establish a value network in which the financial assets can flow. Daily volume of Bitcoin and Ethereum, which are most familiar to us, already exceeds $1 billion. From these data, we can see that the more transaction volume and transaction scale, the higher liquidity. In turn, higher liquidity will increase the quantity of transaction and enhance the value. That will further strengthen financial assets’ value, creating a complete positive feedback mechanism. Therefore liquidity, i.e. transaction frequency and scale, is the first dimension that NR measures.

  • Propagation

Social platforms like WeChat and Facebook have almost 3 billion active users per month. Social platforms’ rapid user growth is a result of the reflection of existing social network and stronger viral growth. In particular, viral transmission, i.e. speed, scope, depth of information transmission and linkage, is the key index to monitor social network’s quality and user growth. In blockchain world, we can see the same pattern. Powerful viral propagation indicates scope and depth of asset liquidity, which can promote the blockchain world’s asset quality and asset scale. Thus, viral transmission, i.e. scope and depth of asset liquidity, is the second dimension that NR measures.

  • Interoperability

At Internet’s early stage, there are only basic websites and private information. Now, information on different platforms can be forwarded on the network, and isolated data silos are gradually being broken. This trend is the process of identifying higher dimensional information. In our point of view, the world of blockchains shall follow a similar pattern, but its speed will be faster. The information on users’ assets, smart contracts, and DApps will become richer, and the interaction of higher dimensional information shall be more frequent, thus better interoperability shall become more and more important. Therefore, NR’s third measure dimension is interoperability.

Based on above-stated dimensions, we start to construct Nebulas’ NR system by drawing from richer data, building a better model, digging up more diversified value dimensions, and establishing a measure of value in blockchain world.

元网络就是含有超映射结构元数据的网络。

全新的共识激励机制

星云激励是自治的基石,提供持久的正向激励。通过 开发者激励协议(Developer Incentive Protocol, DIP) 激励开发者,通过 贡献度证明(Proof of Devotion, PoD) 激励社区。阅读 开发者激励协议紫皮书 了解更多DIP内容。访问 星云节点计划页 了解星云节点计划——基于贡献度证明机制(PoD)。

卓越的升级能力

无需硬分叉即可升级,自进化是自治的未来。 星云原力(Nebulas Force) 提供了无需硬分叉即可升级的能力。

A series of basic protocols such as the NR, the PoD, and the DIP shall become a part of the blockchain data. With the growth of data on Nebulas, these basic protocols will be upgraded, which will avoid fractures between developers and community, as well as a “fork”. We call this fundamental capability of our blockchain “Nebulas Force” (NF).

As the Nebulas community grows, NF and basic protocols’ update ability shall be open to the community. According to users’ NR weight and the community voting mechanism, Nebulas’ evolution direction and its update objectives will be determined by the community. With the help of NF’s core technology and its openness, Nebulas will have an ever-growing evolutive potential and infinite evolving possibilities.

学习资料

星云愿景:让每个人从去中心化协作中公平获益。阅读写在创世区块上的 星云宣言

如果想要了解更多,请关注 官方博客 ,或者访问官网 nebulas.io 以下是主要分类:

采访

星云链创始人徐义吉采访:

星云链(Nebulas)& 小蚁(NEO)创始人,前蚂蚁金服区块链平台部负责人,前谷歌反作弊小组成员,同济大学计算机学士。2013年起创办了中国首个区块链技术社区比特创业营(BitsClub)、中国首个专注于区块链行业投资基金FBG。

星云链联合创始人王冠专访:

星云链(Nebulas)和小蚁(NEO)联合创始人,OpenIP&IP圈发起人,毕业于东南大学,区块链行业连续创业者。

星云团队专访视频:

社区采访:

社区活动

自2017年6月以来,星云社区组织过超过60场社区见面会、黑客马拉松、技术交流分享等多种形式的活动,足迹遍布世界9个国家,20多个城市。走访过加州大学伯克利分校、纽约大学、哥伦比亚大学、哈佛大学、新加坡新跃社科大学、清华大学、北京大学、上海交通大学、同济大学等海内外院校。查看 活动历史 。欢迎 组织本地活动,加入星云生态。

Nebulas Community

使用星云

如果你是开发者,想要开发DApp或者使用主网进行开发,欢迎访问 开发者中心,在本wiki中,可以访问 开发章节,特别是 开发教程 了解更多星云技术,查找开发资源。如果你是普通用户,有4种方法使用星云:

1. 使用在星云上的DApp

你可以在 星云 DApps 商店 (社区用户m5j开发)查找更多DApp。

DApp列表

  • 5月月冠-细胞进化 体验
  • 6月月冠-头号玩家 体验
  • 第6周周冠-NAS Tip Bot 体验
  • 养成类游戏-恐龙乐园 体验
  • 激励计划第7周周冠-四铢 体验
  • 现代世界的魔法战争-隐秘世界OL 体验
  • DApp商店-Nebulas DApps 体验
  • 智能合约浏览器-NAS Smart Contract Explorer 体验
  • 黄金买卖-Gold Token 体验
  • NAS收款-Custom NAS wallet Aliases 体验
  • Twitter小额NAS发送-neby 体验
  • 捐赠NAS挂件-Nebulas donate widget 体验
  • 网页钱包(chrome插件)-web wallet 体验
  • 跨平台智能合约IDE-Nebulas 体验
  • 棋盘游戏-GAME OF BLOCKS 体验
  • 与中心化履约保证-NASESCROW 体验
2. 什么是NAS,如何获得?

星云币(NAS)为星云链唯一原生代币,用于支付星云链上的交易手续费和计算服务费。 点击这里 查看分发情况。星云社区提供原生激励,鼓励开发者和社区成员作出贡献,构建良性经济体和生态。

https://nebulas.io/assets/images/community/token-flow.gif

你可以在交易所买卖NAS, 点击这里 查看交易所列表。你也可以通过 CoinSwitchSWFT Blockchain 币币兑换获得NAS。

你可以通过成为社区贡献者获得NAS。请访问社区协作平台: Go.nebulas

3. 什么是钱包,如何存储NAS?
NAS nano pro

NAS nano 是一款安全、好用的钱包,可以帮助你更好的管理数字资产。

注:iOS版NAS nano请使用中国大陆以外的Apple账户在App store中下载

NAS nano pro钱包有4大特点:

  • 创建、导入和管理钱包快捷方便。
  • 快速查询转帐交易记录。
  • 提供三种钱包备份方式:助记词、Keystore、私钥备份,确保资产安全。
  • 支持NAS和其他NRC20代币,比如NAX、ATP。如果你想把你的币列在NAS nano pro上,请 点击这里

_images/nano_app_capture_en.png

其他钱包

其他支持管理和存储星云币(NAS)的钱包如下:

点击这里 查看以上钱包详情。

4. 什么是NAX,如何获得?

用户通过去中心化质押(dStaking)的方式,质押NAS获得NAX。NAX采用动态分发策略,实际总发行量与全局质押率相关,用户个人获得NAX的数量与NAS质押量以及币龄相关。NAX流转与生态的关联度更紧密,并构成一个正向循环的经济体。访问nextdao.io了解更多。

NAX产生的唯一方式是去中心化质押NAS。但有3种方式可以获得NAX:

通过去中心化质押NAS的方式获得NAX:

从交易所买卖NAX:

闪兑:在SWFT Blockchain上使用其他币种闪兑NAX。

开发

从这里开始

了解关于星云的基本信息请访问官网: nebulas.io 。如果想做深入了解,请阅读 星云技术白皮书星云非技术白皮书 。想为星云做贡献,请访问:如何做贡献

更多文档参见:

设计概览

image0

TODO: More features described in our 技术白皮书, such as NR, PoD, DIP and NF, will be integrated into the framework in later versions very soon.
Core Dataflow

Here is a core workflow example to explain how Nebulas works in current version. For each Nebulas node, it keeps receiving blocks or transactions from network and mining new block locally.

image1

更多细节
区块链
Model

Nebulas use accounts model instead of UTXO model. The execution of transactions will consume gas.

Data Structure
Block Structure
+---------------+----------------+--------------+
|  blockHeader  |  transactions  |  dependency  |
+---------------+----------------+--------------+
blockHeader: header info
transactions: transactions array
dependency: the dependency relationship among transactions

Block Header Structure
+-----------+--------+--------------+------------+-------------+-------+--------+
|  chainid  |  hash  |  parentHash  |  coinbase  |  timestamp  |  alg  |  sign  |
+-----------+--------+--------------+------------+-------------+-------+--------+
+-------------+-----------+--------------+-----------------+
|  stateRoot  |  txsRoot  |  eventsRoot  |  consensusRoot  |
+-------------+-----------+--------------+-----------------+
chainid: chain identity the block belongs to
hash: block hash
parentHash: parent block hash
coinbase: account to receive the mint reward
timestamp: the number of nanoseconds elapsed since January 1, 1970 UTC
alg: the type of signature algorithm
sign: the signature of block hash
stateRoot: account state root hash
txsRoot: transactions state root hash
eventsRoot: events state root hash
consensusRoot: consensus state, including proposer and the dynasty of validators

Transaction Structure
+-----------+--------+--------+------+---------+---------+-------------+
|  chainid  |  hash  |  from  |  to  |  value  |  nonce  |  timestamp  |
+-----------+--------+--------+------+---------+---------+-------------+
+--------+------------+------------+
|  data  |  gasPrice  |  gasLimit  |
+--------+------------+------------+
chainid: chain identity the block belongs to
hash: transaction hash
from: sender's wallet address
to: receiver's wallet address
value: transfer value
nonce: transaction nonce
timestamp: the number of seconds elapsed since January 1, 1970 UTC
alg: the type of signature algorithm
sign: the signature of block hash
data: transaction data, including the type of transaction(binary transfer/deploy smart contracts/call smart contracts) and payload
gasPrice: the price of each gas consumed by the transaction
gasLimit: the max gas that can be consumed by the transaction
Blockchain Update

In our opinion, Blockchain only needs to care about how to process new blocks to grow up safely and efficiently. What‘s more, Blockchain can only get new blocks in the following two channels.

A new block from network

Because of the unstable network latency, we cannot make sure any new block received can be linked to our current Chain directly. Thus, we need the Blocks Pool to cache new blocks.

A new block from local miner

At first, we need the Transactions Pool to cache transactions from network. Then, we wait for a new block created by local Consensus component, such as DPoS.

No matter where a new block comes from, we use the same steps to process it as following.

_images/blockpool.png

World State

Every block contains the current world state, consist of following four states. They are all maintained as Merkle Trees.

Accounts State

All accounts in current block are stored in Accounts State. Accounts are divided into two kinds, normal account & smart contract account.

Normal Account, including

  • wallet address
  • balance
  • nonce: account‘s nonce, it will increment in steps of 1

Smart Contract Account, including

  • contract address
  • balance
  • birth place: the transaction hash where the contract is deployed
  • variables: contains all variables‘ values in the contract
Transactions State

All transactions submitted on chain are storage in Transactions State.

Events State

While transactions are executed, many events will be triggered. All events triggered by transactions on chain are stored in Events State.

Consensus State

The context of consensus algorithm is stored in consensus state.

As for DPoS, the consensus state includes

  • timestamp: current slot of timestamp
  • proposer: current proposer
  • dynasty: current dynasty of validators
Serialization

We choose Protocol Buffers to do general serialization in consideration of the following benefits:

  • Large scale proven.
  • Efficiency. It omits key literals and use varints encoding.
  • Multi types and multilangue client support. Easy to use API.
  • Schema is good format for communication.
  • Schema is good for versioning/extension, i.e., adding new message fields or deprecating unused ones.

Specially, we use json to do serialization in smart contract codes instead of protobuf for the sake of readability.

Synchronization

Sometimes we will receive a block with height much higher than its current tail block. When the gap appears, we need to sync blocks from peer nodes to catch up with them.

Nebulas provides two method to sync blocks from peers: Chunks Downloader and Block Downloader. If the gap is bigger than 32 blocks, we‘ll choose Chunk Downloader to download a lot of blocks in chunks. Otherwise, we choose Block Downloader to download block one by one.

Chunks Downloader

Chunk is a collection of 32 successive blocks. Chunks Downloader allows us to download at most 10 chunks following our current tail block each time. This chunk-based mechanism could help us minimize the number of network packets and achieve better safety.

The procedure is as following,

1. A sends its tail block to N remote peers.
2. The remote peers locate the chunk C that contains A's tail block.
   Then they will send back the headers of 10 chunks, including the chunk C and 9 C's subsequent chunks, and the hash H of the 10 headers.
3. If A receives >N/2 same hash H, A will try to sync the chunks represented by H.
4. If A has fetched all chunks represented by H and linked them on chain successfully, Jump to 1.

In steps 1~3, we use majority decision to confirm the chunks on canonical chain. Then we download the blocks in the chunks in step 4.

Note: ChunkHeader contains an array of 32 block hash and the hash of the array. ChunkHeaders contains an array of 10 ChunkHeaders and the hash of the array.

Here is a diagram of this sync procedure:

_images/the-diagram-of-sync-process.png

Block Downloader

When the length gap between our local chain with the canonical chain is smaller than 32, we‘ll use Block downloader to download the missing blocks one by one.

The procedure is as following,

1. C relays the newest block B to A and A finds B's height is bigger than current tail block's.
2. A sends the hash of block B back to C to download B's parent block.
3. If A received B's parent block B', A will try to link B' with A's current tail block.
   If failed again, A will come back to step 2 and continue to download the parent block of B'. Otherwise, finished.

This procedure will repeat until A catch up with the canonical chain.

Here is a diagram of this download procedure:

_images/the-diagram-of-download-process.png

Merkle Patricia Tree
Basic: Radix Tree

A Radix Tree using address as the key looks like below:

  • Addresses are represented as Hex Characters
  • Each node in the Tree is a 16-elements array, 16 branch-slots(0123...def)
  • leaf node: value can be any binary data carried by the address
  • non-leaf node: value is the hash value calculated based on the children’s data

As for a 160-bits address, the max height of the tree is 40

_images/radix_tree.png

Problems: much space for a single entry 40 steps for each lookup

Advanced: Merkle Patricia Tree

In order to reduce the storage of Radix Tree. The nodes in Merkle Patricia Tree are divided into three kinds,

  • extension node: compress nodes using common prefix
  • leaf node: compress nodes using unique suffix
  • branch node: same as node in Radix Tree

_images/merkle_tree.png

How to store Merkle Patricia Tree

Key/Value Storage

hash(value) = sha3(serialize(value))

key = hash(value)

How to update Merkle Patricia Tree

Query

DFS from top to bottom

Update, Delete or Insert

1.Query the node from top to bottom

2.update the hash along the path from bottom to top

_images/merkle_tree_update.png

Performance Each operation costs O(log(n))

How to verify using Merkle Patricia Tree

Theorems

1.Same merkle trees must have same root hash.

2.Different merkle trees must have different root hash.

Using the theorems, we can verify the result of the execution of transactions.

Quick Verification

A light client, without sync huge transactions, can immediately determine the exact balance and status of any account by simply asking the network for a path from the root to the account node.

共识

We think each consensus algorithm can be described as the combination of State Machine and Fork Choice Rules.

DPoS(Delegate Proof-of-Stake)
Notice For Nebulas, the primary consensus algorithm should be PoD, the DPoS algorithm is just a temporary solution. After the formal verification of PoD algorithm, we will transition mainnet to PoD. All witness (bookkeeper/miner) of DPoS are now accounts officially maintained by Nebulas. We will make sure a smooth transition from DPoS to PoD. We will create new funds to manage all the rewards of bookkeeping. And we will NOT sell those NAS on exchanges. All NAS will be used for building the Nebulas ecosystem, for example, rewarding DApp developers on Nebulas. And we will provide open access to all the spending of these rewards periodically.

As for the DPoS in Nebulas, it can also be decribed as a state machine.

State Machine

_images/dpos.png

Fork Choice Rules
  1. Always choose the longest chain as the canonical chain.
  2. If A and B has the same length, we choose the one with smaller hash.
PoD (Proof-of-Devotion)

Here is a draft of PoD. The research on PoD is ongoing here.

State Machine

_images/pod.png

Fork Choice Rules
  1. Always to choose the chain with highest sum of commit votes.
  2. If A and B has the same length, we choose the one with smaller hash.
Transaction Process Diagram

When a transaction is submitted, it is necessary to check the chain in the transaction. Transactions that are submitted externally or have been packaged into the block are somewhat different when doing validation.

New Transaction Process (from network, rpc)

Transactions submitted through an RPC or other node broadcast.

  • Api SendRawTransaction Verification below steps when exist fail, then return err
  • check whether fromAddr and toAddr is valid (tx proto verification)
  • check len of Payload <= MaxDataPayLoadLength (tx proto verification)
  • 0 < gasPrice <= TransactionMaxGasPrice and 0 < gasLimit <= TransactionMaxGas (tx proto verification)
  • check Alg is SECP256K1 (tx proto verification)
  • chainID Equals, Hash Equals, Sign verify??; fail and drop;
  • check nonceOfTx > nonceOfFrom
  • check Contract status is ExecutionSuccess if type of tx is TxPayloadCallType, check toAddr is equal to fromAddr if type of tx is TxPayloadDeployType
  • Transaction pool Verification
  • gasPrice >= minGasPriceOfTxPool & 0 < gasLimit <= maxGasLimitOfTxPool??; fail and drop;
  • chainID Equals, Hash Equals, Sign verify??; fail and drop;
Transaction in Block Process

The transaction has been packaged into the block, and the transaction is verified after receiving the block.

  • Packed
  • Nonce Verification: nonceOfFrom +1 == nonceOfTx ??; nonceOfTx < nonceOfFrom +1 fail and drop, nonceOfTx > nonceOfFrom +1 fail and giveback to tx pool;
  • check balance >= gasLimit * gasPrice ??; fail and drop;
  • check gasLimit >= txBaseGas(MinGasCountPerTransaction + dataLen*GasCountPerByte) ??; fail and drop;
  • check payload is valid ??; fail and submit; gasConsumed is txBaseGas ( all txs passed the step tx will be on chain)
  • check gasLimit >= txBaseGas + payloasBaseGas(TxPayloadBaseGasCount[payloadType]) ??;fail and submit; gasConsumed is txGasLimit
  • check balance >= gasLimit * gasPrice + value ??;fail and submit; gasConsumed is txBaseGas + payloadsBaseGas
  • transfer value from SubBalance and to AddBalance ??;fail and submit; gasConsumed is txBaseGas + payloadsBaseGas
  • check gasLimit >= txBaseGas + payloadsBaseGas + gasExecution ??;fail and submit; gasConsumed is txGasLimit
  • success submit gasConsumed is txBaseGas + payloadsBaseGas + gasExecution
  • Verify
  • check whether fromAddr and toAddr is valid (tx proto verification) ??; fail and submit;
  • check len of Payload <= MaxDataPayLoadLength (tx proto verification) ??; fail and submit;
  • 0 < gasPrice <= TransactionMaxGasPrice and 0 < gasLimit <= TransactionMaxGas (tx proto verification)
  • check Alg is SECP256K1 (tx proto verification) ??; fail and submit;
  • chainID Equals, Hash Equals, Sign verify??; fail and drop;
  • Next steps like Transaction Packed in Block Process.
Event functionality

The Event functionality is used to make users or developers subscribe interested events. These events are generated during the execution of the blockchain, and they record the key execution steps and execution results of the chain. To query and verify the execution results of transactions and smart contracts, we record these two types of events into a trie and save them to the chain.

Event structure:

type Event struct {
    Topic string // event topic, subscribe keyword
    Data  string // event content, a json string
}

After a event is generated, it will be collected for processing in eventEmitter. Users can use the emitter subscription event. If the event is not subscribed, it will be discarded, and for the event that has been subscribed, the new event will be discarded because of the non-blocking mechanism, if the channel is not blocked in time.

Event Reference
TopicNewTailBlock

This event occurs when the tail block of the chain is updated.

  • Topic:chain.newTailBlock
  • Data:
    • height: block height
    • hash: block hash
    • parent_hash: block parent hash
    • acc_root: account state root hash
    • timestamp: block timestamp
    • tx: transaction state root hash
    • miner: block miner
TopicRevertBlock

This event occurs when a block is revert on the chain.

  • Topic:chain.revertBlock
  • Data: The content of this topic is like TopicNewTailBlock data.
TopicLibBlock

This event occurs when the latest irreversible block change.

  • Topic:chain.latestIrreversibleBlock
  • Data: The content of this topic is like TopicNewTailBlock data.
TopicPendingTransaction

This event occurs when a transaction is pushed into the transaction pool.

  • Topic:chain.pendingTransaction
  • Data:
    • chainID: transaction chain id
    • hash: transaction hash
    • from: transaction from address string
    • to: transaction to address string
    • nonce: transaction nonce
    • value: transaction value
    • timestamp: transaction timestamp
    • gasprice: transaction gas price
    • gaslimit: transaction gas limit
    • type: trsnaction type
TopicTransactionExecutionResult

This event occurs when the end of a transaction is executed. This event will be recorded on the chain, and users can query with RPC interface GetEventsByHash.

This event records the execution results of the transaction and is very important.

  • Topic:chain.transactionResult
  • Data:
    • hash: transaction hash
    • status: transaction status, 0 failed, 1success, 2 pending
    • gasUsed: transaction gas used
    • error: transaction execution error. If the transaction is executed successfully, the field is empty.
EventNameSpaceContract

This event occurs when the contract is executed. When the contract is executed, the contract can record several events in the execution process. If the contract is successful, these events will be recorded on the chain and can be subscribed, and the event of the contract will not be recorded at the time of the failure. This event will also be recorded on the chain, and users can query with RPC interface GetEventsByHash.

  • Topic:chain.contract.[topic] The topic of the contract event has a prefix chain.contract., the content is defined by the contract writer.
  • Data: The content of contract event is defined by contract writer.
Subscribe

All events can be subscribed and the cloud chain provides a subscription RPC interface Subscribe. It should be noted that the event subscription is a non-blocking mechanism. New events will be discarded when the RPC interface is not handled in time.

Query

Only events recorded on the chain can be queried using the RPC interface GetEventsByHash. Current events that can be queried include:

交易Gas

In Nebulas, either a normal transaction which transfer balance or a smart contract deploy & call burns gas, and charged from the balance of from address. A transaction contains two gas parameters gasPrice and gasLimit :

  • gasPrice: the price of per gas.
  • gasLimit: the limit of gas use.

The actual gas consumption of a transaction is the value: gasPrice * gasUsed, which will be the reward to the miner coinbase. The gasUsed value must less than or equal to the gasLimit. Transaction‘s gasUsed can be estimate by RPC interface estimategas and store in transaction‘s execution result event.

Design reason

Users want to avoid gas costs when the transaction is packaged. Like Bitcoin and Ethereum, Nebulas GAS is used for transaction fee, it have two major purposes:

  • As a rewards for minter, to incentive them to pack transactions. The packaging of the transaction costs the computing resources, especially the execution of the contract, so the user needs to pay for the transaction.
  • As a cost for attackers. The DDOS attach is quite cheap in Internet, black hackers hijack user‘s computer to send large network volume to target server. In Bitcoin and Ethereum network, each transaction must be paid, that significant raise the cost of attack.
Gas constitution

When users submit a transaction, gas will be burned at these aspects:

  • transaction submition
  • transaction data storage
  • transaction payload addition
  • transaction payload execution(smart contract execution)

In all these aspects, the power and resources of the net will be consumed and the miners will need to be paid.

Transaction submition

A transaction‘s submition will add a transaction to the tail block. Miners use resources to record the deal and need to be paid. It will burn a fixed number of gas, that would be defined in code as the following:

// TransactionGas default gas for normal transaction
TransactionGas = 20000

If the transaction verifies failed, the gas and value transfer will rollback.

Transaction data storage

When deploying a contract or call contract‘s method, the raw data of contract execution save in the transaction‘s data filed, which cost the storage of resources on the chain. A formula to calculate gas:

TransactionDataGas = 1

len(data) * TransactionDataGas

The TransactionDataGas is a fixed number of gas defined in code.

Different types of transactions‘ payload have different gas consumption when executed. The types of transactions currently supported by nebulas are as follows:

  • binary: The binary type of transaction allows users to attach binary data to transaction execution. These binary data do not do any processing when the transaction is executed.
    • The fixed number of gas defined 0.
  • deploy & call: The deploy and call type of transaction allows users to deploy smart contract on nebulas. Nebulas must start nvm to execute the contract, so these types of transction must paid for the nvm start.
    • The fixed number of gas defined 60.
Transaction payload execution(Smart contract deploy & call)

The binary type of transaction do not do any processing when the transaction is executed, so the execution need not be paid.

When a smart contract deploys or call in transaction submition, the contract execution will consume miner‘s computer resources and may store data on the chain.

  • execution instructions: Every contract execution cost the miner‘s computer resources, the v8 instruction counter calculates the execution instructions. The limit of execution instructions will prevent the excessive consumption of computer computing power and the generation of the death cycle.
  • contract storage: The smart contract‘s LocalContractStorage which storage contract objects also burn gas. Only one gas per 32 bytes is consumed when stored(set/put), get or delete not burns gas.

The limit of contract execution is:

    gasLimit - TransactionGas - len(data) * TransactionDataGas - TransactionPayloadGasCount[type]
Gas Count Matrix

The gas count matrix of smart contract execution

Operator Gas Count/Opt. Description
Binary 1 Binary & logical operator
Load 2 Load from memory
Store 2 Save to memory
Return 2 Return value, save to memory
Call (inner) 4 Call functions in the same Smart Contract
Call (external) 100 Call functions from other Smart Contract

| Expression | Sample Code | Binary Opt. | Load Opt. | Store Opt. | Return Opt. | Call (inner) Opt. | Gas Count | | | | — | :— | —: | —: | —: | —: | —: | —: | | CallExpression | a(x, y) | 0 | 0 | 1 | 1 | 1 | 8 | | | | AssignmentExpression | x&=y | 1 | 0 | 1 | 0 | 0 | 3 | | | | BinaryExpression | x==y | 1 | 0 | 0 | 1 | 0 | 3 | | | | UpdateExpression | x++ | 1 | 0 | 1 | 0 | 0 | 3 | | | | UnaryExpression | x+y | 1 | 0 | 0 | 1 | 0 | 3 | | | | LogicalExpression | x | | y | 1 | 0 | 0 | 1 | 0 | 3 | | MemberExpression | x.y | 0 | 1 | 0 | 1 | 0 | 4 | | | | NewExpression | new X() | 0 | 0 | 1 | 1 | 1 | 8 | | | | ThrowStatement | throw x | 0 | 0 | 0 | 1 | 1 | 6 | | | | MetaProperty | new.target | 0 | 1 | 0 | 1 | 0 | 4 | | | | ConditionalExpression | x?y:z | 1 | 0 | 0 | 1 | 0 | 3 | | | | YieldExpression | yield x | 0 | 0 | 0 | 1 | 1 | 6 | | | | Event | | 0 | 0 | 0 | 0 | 0 | 20 | | | | Storage | | 0 | 0 | 0 | 0 | 0 | 1 gas/bit | | |

Tips

In nebulas, the transaction pool of each node has a minimum and maximum gasPrice and maximum gasLimit value. If transaction‘s gasPrice is not in the range of the pool‘s gasPrice or the gasLimit greater than the pool‘s gasLimit the transaction will be refused.

Transaction pool gasPrice and gasLimit configuration:

  • gasPrice
    • minimum: The minimum gasPrice can be set in the configuration file. If the minimum value is not configured, the default value is 20000000000(2*10^10).
    • maximum: The maximum gasPrice is 1000000000000(10^12), transaction pool‘s maximum configuration and transaction‘s gasPrice can‘t be overflow.
  • gasLimit
    • minimum: The transaction‘s minimum gasLimit must greater than zero.
    • maximum: The maximum gasPrice is 50000000000(50*10^9), transaction pool‘s maximum configuration and transaction‘s gasLimit can‘t be overflow.
Logs
Introduction

Nebulas provides two kinds of logs: console log & verbose log.

Console Log

Console Log(CLog) is used to help you understand which job Neb is working on now, including start/stop components, receive new blocks on chain, do synchronization and so on.

  • CLog will print all logs to stdout & log files both. You can check them in your standard output directly.

Nebulas console log statements

// log level can be `Info`,`Warning`,`Error`
logging.CLog().Info("")
Startup specifications

Nebulas start service should give a console log, the logs should before the service start. The log format just like this:

logging.CLog().Info("Starting xxx...")
Stopping specifications

Nebulas stop service should give a console log, the logs should before the service stoped. The log format just like this:

logging.CLog().Info("Stopping xxx...")
Verbose Log

Verbose Log(VLog) is used to help you understant how Neb works on current job, including how to verifiy new blocks, how to discover new nodes, how to mint and so on.

  • VLog will print logs to log files only. You can check them in your log folders if needed.

What‘r more, you can set your concerned level to VLog to filter informations. The level filter follows the priority as Debug < Info < Warn < Error < Fatal.

Hookers

By default, Function hookers & FileRotate hookers are added to CLog & VLog both.

FunctionNameHooker

FunctionHooker will append current caller‘s function name & code line to the loggers. The result looks like this,

time=“2018-01-03T20:20:52+08:00“ level=info msg=“node init success“ file=net_service.go func=p2p.NewNetManager line=137 node.listen=“[0.0.0.0:10001]“
FileRotateHooker

FileRotateHooker will split logs into many smaller segments by time. By default, all logs will be rotated every 1 hour. The log folder looks like this,

neb-2018010415.log neb-2018010416.log neb.log -> /path/to/neb-2018010415.log

If you have any suggestions about logs, please feel free to submit issues on our wiki repo. Thanks!

星云地址设计

Nebulas address system is carefully designed. As you will see below, both account and smart contract address are strings starting with a “n“, which could be thought of as our faith Nebulas/NAS.

Account Address

Similar to Bitcoin and Ethereum, Nebulas also adopts elliptic curve algorithm as its basic encryption algorithm for Nebulas accounts. The address is derived from public key, which is in turn derived from the private key that encrypted with user‘s passphrase.Also we have the checksum design aiming to prevent a user from sending Nas to a wrong user account accidentally due to entry of several incorrect characters.

The specific calculation formula is as follows:

1.  content = ripemd160(sha3_256(public key))
    length: 20 bytes
                         +--------+--------+------------------+
2.  checksum = sha3_256( |  0x19  +  0x57  |      content     | )[:4]
                         +--------+--------+------------------+
    length: 4 bytes

                        +--------+---------+-----------------+------------+
3.  address = base58( |    0x19  |  0x57   |     content     |  checksum  | )
                        +--------+---------+-----------------+------------+
    length: 35 chars

0x57 is a one-byte “type code“ for account address, 0x19 is a one-byte fixed “padding“

At this stage, Nebulas just adopts the normal bitcoin base58 encoding schema. A valid address is like: n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC

Smart Contract Address

Calculating contract address differs slightly from account, passphrase of contract sender is not required but address & nonce. For more information, please check smart contract and rpc.sendTransaction. Calculation formula is as follows:

1.  content = ripemd160(sha3_256(tx.from, tx.nonce))
    length: 20 bytes
                         +--------+--------+------------------+
2.  checksum = sha3_256( |  0x19  |  0x58  +      content     | )[:4]
                         +--------+--------+------------------+
    length: 4 bytes

                      +--------+---------+-----------------+------------+
3.  address = base58( |  0x19  |  0x58   |     content     |  checksum  | )
                      +--------+---------+-----------------+------------+
    length: 35 chars

0x58 is a one-byte “type code“ for smart contract address, 0x19 is a one-byte fixed “padding“

A valid address is like: n1sLnoc7j57YfzAVP8tJ3yK5a2i56QrTDdK

DIP (TBD)

基础设施
Network Protocol

For the network protocol, there are lots of existing solution. However, the Nebulas Team finally decides to define our own wire protocol, ensures the following principles to design the protocol:

  • the protocol should be simple and straight.
  • the message can be verified before receiving all package, fail early.
  • the protocol should be debugging friendly, developer can easily understand the raw message.
Protocol

In Nebulas, we define our own wire protocol, as the following:

 0               1               2               3              (bytes)
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Magic Number                          |
+---------------------------------------------------------------+
|                         Chain ID                              |
+-----------------------------------------------+---------------+
|                         Reserved              |   Version     |
+-----------------------------------------------+---------------+
|                                                               |
+                                                               +
|                         Message Name                          |
+                                                               +
|                                                               |
+---------------------------------------------------------------+
|                         Data Length                           |
+---------------------------------------------------------------+
|                         Data Checksum                         |
+---------------------------------------------------------------+
|                         Header Checksum                       |
|---------------------------------------------------------------+
|                                                               |
+                         Data                                  +
.                                                               .
.                                                               .
|                                                               |
+---------------------------------------------------------------+
  • Magic Number: 32 bits (4 chars)
    • The protocol magic number, A constant numerical or text value used to identify protocol.
    • Default: 0x4e, 0x45, 0x42, 0x31
  • Chain ID: 32 bits
    • The Chain ID is used to distinguish the test network and the main network.
  • Reserved: 24 bits
    • reserved field.
    • The first bit indicates whether the network message is compressed.
    • compressed: {0x80, 0x0, 0x0}; uncompressed: {0x0, 0x0, 0x0}
  • Version: 8 bits
    • The version of the Message Name.
  • Message Name: 96 bits (12 chars)
    • The identification or the name of the Message.
  • Data Length: 32 bits
    • The total length of the Data.
  • Data Checksum: 32 bits
    • The CRC32 checksum of the Data.
  • Header Checksum: 32 bits
    • The CRC32 checksum of the fields from Magic Number to Data Checksum, totally 256 bits.
  • Data: variable length, max 512M.
    • The message data.

We always use Big-Endian in message protocol.

Handshaking Messages
  • Hello

the handshaking message when peer connect to others.

version: 0x1

data: struct {
    string node_id  // the node id, generated by underlying libp2p.
    string client_version // the client version, x.y.z schema, eg. 0.1.0.
}
  • OK

the response message for handshaking.

version: 0x1

data: struct {
    string node_id // the node id, generated by underlying libp2p.
    string node_version // the client version, x.y.z schema, eg. 0.1.0.
}
  • Bye

the message to close connection.

version: 0x1
data: struct {
    string reason
}
Networking Messages
  • NetSyncRoutes

request peers to sync route tables.

version: 0x1
  • NetRoutes

contains the local route tables.

version: 0x1
data: struct {
    PeerID[] peer_ids // router tables.
}

struct PeerID {
    string node_id  // the node id.
}
Nebulas Messages

TBD.

Crypto Design Doc

Similar to Bitcoin and Ethereum, Nebulas also adopted an elliptic curve algorithm as its basic encryption algorithm for Nebulas transactions. Users’ private keys will be encrypted with their passphrases and stored in a keystore.

Hash

Supports generic hash functions, like sha256, sha3256 and ripemd160 etc.

Keystore

The Nebulas Keystore is designed to manage user’s keys.

Key

The Key interface is designed to support various keys, including symmetric keys and asymmetric keys.

Provider

The Keystore provides different methods to save keys, such as memory_provider and persistence_provider. Before storage, the key has been encrypted in the keystore.

  • memory provider: This type of provider keeps the keys in memory. After the key has been encrypted with the passphrase when user setkey or load, it is cached in memory provider.
  • persistence provider: This type of provider serializes the encrypted key to the file. The file is compatible with Ethereum’s keystore file. Users can back up the address with its privatekey in it.
Signature

The Signature interface is used to provide applications with the functionality of a digital signature algorithm. A Signature object can be used to generate and verify digital signatures.

There are two phases, in order to use a Signature object for signing data :

  • Initialization: with a private key, which initializes the signature for signing (see initSign() in the source code of go-nebulas).
  • Signing of all input bytes.

A Signature object can recover the public key with a signature and the plain text that was signed (see function RecoverSignerFromSignature in go-nebulas). So just comparing the from address and the address derived from the public key can verify a transaction

Similar to the Android Keystore, TPM, TEE and hardware low level security protection will be supported as a provider later.
NVM - Nebulas Virtual Machine

NVM is one of the most important components in Nebulas. As the name implies, it provides managed virtual machine execution environments for Smart Contract and Protocol Code.

go-nebulas now support two kinds of Virtual Machines:

Nebulas V8 Engine

In go-nebulas, we designed and implemented the Nebulas V8 Engine based on Chrome V8.

The Nebulas V8 Engine provides a high performance sandbox for Smart Contract execution. It guarantees user deployed code is running in a managed environment, and prevents massive resource consumption on hosts. Owing to the use of Chrome V8, JavaScript and TypeScript are first-class languages for Nebulas Smart Contracts. Anyone familiar with JavaScript or TypeScript can write their own Smart Contract and run it in Nebulas V8.

The following content is an example of Smart Contract written in JavaScript:

"use strict";

var BankVaultContract = function() {
    LocalContractStorage.defineMapProperty(this, "bankVault");
};

// save value to contract, only after height of block, users can takeout
BankVaultContract.prototype = {
    init:function() {},
    save:function(height) {
        var deposit = this.bankVault.get(Blockchain.transaction.from);
        var value = new BigNumber(Blockchain.transaction.value);
        if (deposit != null && deposit.balance.length > 0) {
            var balance = new BigNumber(deposit.balance);
            value = value.plus(balance);
        }
        var content = {
            balance:value.toString(),
            height:Blockchain.block.height + height
        };
        this.bankVault.put(Blockchain.transaction.from, content);
    },
    takeout:function(amount) {
        var deposit = this.bankVault.get(Blockchain.transaction.from);
        if (deposit == null) {
            return 0;
        }
        if (Blockchain.block.height < deposit.height) {
            return 0;
        }
        var balance = new BigNumber(deposit.balance);
        var value = new BigNumber(amount);
        if (balance.lessThan(value)) {
            return 0;
        }
        var result = Blockchain.transfer(Blockchain.transaction.from, value);
        if (result > 0) {
            deposit.balance = balance.dividedBy(value).toString();
            this.bankVault.put(Blockchain.transaction.from, deposit);
        }
        return result;
    }
};

module.exports = BankVaultContract;

For more information about smart contracts in Nebulas, please go to Smart Contract.

For more information about the design of the Nebulas V8 Engine, please go to Nebulas V8 Engine.

LLVM

TBD.

Permission Control in Smart Contract
What Is Permission Control of Smart Contract

The permission control of a smart contract refers to whether the contract caller has permission to invoke the function in the contract. There are two types of permission control: owner permission control and other permission control.

Owner permissions control: Only the creator of the contract can call this method, other callers can not call the method.

Other permission control: The contract method can be invoked if the contract developer defines a conditional caller according to the contract logic. Otherwise, it cannot be invoked.

Owner Permission Control

If you want to specify a owner to a small contract and wish if some functions could be called only by the owner and no one else. You can use following lines of code in your smart contract.

"use strict";
var onlyOwnerContract = function () {
    LocalContractStorage.defineProperty(this, "owner");
};
onlyOwnerContract.prototype = {
  init: function() {
          this.owner=Blockchain.transaction.from;
  },
  onlyOwnerFunction: function(){
          if(this.owner==Blockchain.transaction.from){
              //your smart contract code
              return true;
          }else{
              return false;
          }
  }
};
module.exports = BankVaultContract;

Explanation:

The function init is only called once when the contract is deployed, there you specify the owner of the contract.The onlyOwnerFunctiuon ensures that the function is called by the owner of contact.

Other Permission Control

In your smart contract, if you need to specify other permission control to a smart contract. For example, in your smart contract, you need to verify the transaction value in your smart contract. you can write your smart contract in the following way.

'use strict';
var Mixin = function () {};
Mixin.UNPAYABLE = function () {
   if (Blockchain.transaction.value.gt(0)) {
       return false;
   }
   return true;
};
Mixin.PAYABLE = function () {
   if (Blockchain.transaction.value.gt(0)) {
       return true;
   }
   return false;
};
Mixin.POSITIVE = function () {
   console.log("POSITIVE");
   return true;
};
Mixin.UNPOSITIVE = function () {
   console.log("UNPOSITIVE");
   return false;
};
Mixin.decorator = function () {
   var funcs = arguments;
   if (funcs.length < 1) {
       throw new Error("mixin decorator need parameters");
   }
   return function () {
       for (var i = 0; i < funcs.length - 1; i ++) {
           var func = funcs[i];
           if (typeof func !== "function" || !func()) {
               throw new Error("mixin decorator failure");
           }
       }
       var exeFunc = funcs[funcs.length - 1];
       if (typeof exeFunc === "function") {
           exeFunc.apply(this, arguments);
       } else {
           throw new Error("mixin decorator need an executable method");
       }
   };
};
var SampleContract = function () {
};
SampleContract.prototype = {
   init: function () {
   },
   unpayable: function () {
       console.log("contract function unpayable:", arguments);
   },
   payable: Mixin.decorator(Mixin.PAYABLE, function () {
       console.log("contract function payable:",arguments);
   }),
   contract1: Mixin.decorator(Mixin.POSITIVE, function (arg) {
       console.log("contract1 function:", arg);
   }),
   contract2: Mixin.decorator(Mixin.UNPOSITIVE, function (arg) {
       console.log("contract2 function:", arg);
   }),
   contract3: Mixin.decorator(Mixin.PAYABLE, Mixin.POSITIVE, function (arg) {
       console.log("contract3 function:", arg);
   }),
   contract4: Mixin.decorator(Mixin.PAYABLE, Mixin.UNPOSITIVE, function (arg) {
       console.log("contract4 function:", arg);
   })
};
module.exports = SampleContract;

Explanation:

Mixin.UNPAYABLE,Mixin.PAYABLE,Mixin.POSITIVE ,Mixin.UNPOSITIVE are permission control function。The permission control function as follows:

  • Mixin.UNPAYABLE: check the transaction sent value, if value is less than 0 return true, otherwise returns false
  • Mixin.UNPAYABLE : check the transaction sent value, if value is greater than 0 return true, otherwise returns false
  • Mixin.UNPOSITIVE :output log UNPOSITIVE
  • Mixin.POSITIVE :output log POSITIVE

Implement permission control in Mixin.decorator:

  • check arguments: if (funcs.length < 1)
  • invoke permission control function: if (typeof func !== “function“ || !func())
  • if permission control function success ,invoke other function: var exeFunc = funcs[funcs.length - 1]

Permission control tests in smart contracts are as follows:

  • The permission control function of the contract1 is Mixin.POSITIVE. If the permission check passes, the output is printed, otherwise the error is thrown by the permission check function.

          contract1: Mixin.decorator(Mixin.POSITIVE, function (arg) {
              console.log("contract1 function:", arg);
          })
    
  • The permission control function of the contract2 is Mixin.UNPOSITIVE. If the permission check passes, the output is printed, otherwise the error is thrown by the permission check function.

          contract2: Mixin.decorator(Mixin.UNPOSITIVE, function (arg) {
                    console.log("contract2 function:", arg);
          })
    
  • The permission control function of the contract3 is Mixin.PAYABLE, Mixin.POSITIVE. If the permission check passes, the output is printed, otherwise the error is thrown by the permission check function.

         contract3: Mixin.decorator(Mixin.PAYABLE, Mixin.POSITIVE, function (arg) {
                    console.log("contract3 function:", arg);
          })
    
  • The permission control function of the contract4 is Mixin.PAYABLE, Mixin.UNPOSITIVE. If the permission check passes, the output is printed, otherwise the error is thrown by the permission check function.

          contract4: Mixin.decorator(Mixin.PAYABLE, Mixin.UNPOSITIVE, function (arg) {
                     console.log("contract4 function:", arg);
          })
    

Tips:

With reference to the above example, the developer needs only three steps to implement other permission controls:

  • Implement permission control functions.
  • Implement the decorator function, and the permission check is completed by the conditional statement if (typeof func !== “function“ || !func()).
  • Refer to the contract1 function to implement other permission control.
NBRE Design Doc

NBRE (Nebulas Runtiome Environment) is the Nebulas chain execution environment. Its framework is shown as follows.

_images/NBRE-Overview.png

NBRE contains two main processes, which provide the methods how to update algorithms and how to execute algorithms.

The updating process provides how to upload algorithms and core protocols. It includes the following steps:

  1. The algorithms are implemented with the languages supported by LLVM. Then, their codes are handled by the NASIR tool, which are translated to bitcode.
  2. The bitcode streams are coded with base64, which are translated to payload of transaction data. The transaction data is uploaded to the online chain.
  3. After that, the transaction data will be packed and varified. Then, the related bitcode will stored into the RocksDB.

The execution process exhibits the processes from request to results. The corresponding details are as follows.

  1. User appries for algorithm call requests with the forms of RPC or RESful API.
  2. After receiving the request, the core NEB forward it to NBRE.
  3. NBRE starts JIT and loads the algorithm code into JIT.
  4. The JIT executes the algorithm with specified parameters and the invoking method, and returns the execution result.
  5. NBRE returns the execution result to NEB through IPC.
  6. NEB returns the result to the user.
IPC

IPC is the messenger for NEB and NBRE interaction.

Features

IPC adopts shared memoty to communicate between NEB and NBRE to improve performance. There are two sub-threads, a server and a client, inside IPC. The server listens for the NEB request, and the client listens for the NBRE result. Also, there is communication interaction between the two threads.

Framework

The framework of IPC is shown as below.

_images/NBRE-IPC.png
  1. NEB calls a function, and the server receives the request and sends it to the client.
  2. The client sends the request to NBRE.
  3. NBRE runs the corresponding program and returns the result to the client, the client sends the result to the server.
  4. The server returns the result to the NEB.
JIT

JIT is a concurrent virtual machine based on LLVM, which runs ir programs providing algorithms and interfaces for NBRE. It is the key of the dynamic update for NBRE.

Features

Dynamic update

The dynamic update in NBRE contains two respects: - NBRE’s own dynamic update - NBRE’s new feature interfaces

NBRE’s updates are performed by adding algorithms and interface programs to the database. When a new function is updated or called, the corresponding program will be loaded into the JIT in the database.

Concurrent virtual machine

To improve performace, JIT is implemented based on a concurrent virtual machine mechanism. When one interface is called, the JIT first queries whether the corresponding program has been loaded. If the programs is loaded, sets its execution count to be 1800; otherwise, loads the program from database and sets its execution count to be 1801. Then runs the corresponding progrm. At regular intervals, the JIT decrements the corresponding count of each loaded function by one and releases the program with a count when its count less than zero.

Framework

The JIT framework is shown as below.

_images/NBRE-JIT.png
  1. One interface is requested from outside.
  2. JIT queries the corresponding function program from the database.
  3. JIT loads the corresponding program.
  4. Runs the program.
  5. Returns the result.
加入星云

加入星云?从这里入手:

加入星云主网

星云主网 3.0(Nebulas Voyager)已经发布。 本教程将教您如何加入和使用星云主网。

编译

需要首先构建星云主网的可执行文件和依赖库:

  • NEB: 星云主网的主进程

有关编译的详细信息,请参阅 教程

配置文件

主网配置文件位于文件夹 mainnet/conf, 包括:

  • genesis.conf
  • config.conf
  • Miner config
1 genesis.conf

关于创世块的所有可配置信息都在genesis.conf中定义,包括:

  • meta.chain_id: chain identity
  • consensus.dpos.dynasty: the initial dynasty of validators
  • token_distribution: the initial allocation of tokens

注意: 不要修改 genesis.conf。

2 config.conf

有关运行时的所有可配置信息都在config.conf中定义。

请查看 template.conf 查找有关运行时配置的更多详细信息。

注意: 官方种子节点信息如下:

seed:["/ip4/52.76.103.107/tcp/8680/ipfs/Qmbi1NVTYHkeuST2wS3B3aHiTLHDajHZpoZk5EDpAXt9H2","/ip4/52.56.55.238/tcp/8680/ipfs/QmVy9AHxBpd1iTvECDR7fvdZnqXeDhnxkZJrKsyuHNYKAh","/ip4/34.198.52.191/tcp/8680/ipfs/QmQK7W8wrByJ6So7rf84sZzKBxMYmc1i4a7JZsne93ysz5"]
3 miner config

节点可以参与挖矿并获得奖励。挖矿节点需要允许访问出块地址和激励地址(coinbase)。

出块配置文件示例:

chain {
  # mainnet chainID
  chain_id: 1
  # mainnet datadir, should be different with private chain
  datadir: "mainnet/data.db"
  keydir: "keydir"
  # mainnet genesis.conf
  genesis: "mainnet/conf/genesis.conf"
  # mainnet dynasty.conf
  dynasty: "mainnet/conf/dynasty.conf"
  
  # start mine
  start_mine: true
  # receive the mining award, must change to your address
  coinbase: "n1XkoVVjswb5Gek3rRufqjKNpwrDdsnQ7Hq"
  # block signature address, needs to be placed in the node's configuration `keydir`. Also make sure that the address is the node address at the time of registration
  miner: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE"
  # 
  passphrase: "passphrase"

  signature_ciphers: ["ECC_SECP256K1"]
}
数据同步

因为星云主网2018年3月上线,已经运行了一段时间,需要一定时间来同步主网历史数据。为了方便开发者,我们提供离线数据包方便下载。该数据包已经包含了超过一百万的区块,你可以直接点击以下链接快速下载(选择对你来说最快的方法):

注意:数据包必须放在你的config.conf文件里指定的datadir目录下。数据包更新于3.0发布前夕,缺失的块会自动追块。

API 列表

Main Endpoint:

API URL Protocol
RESTful https://mainnet.nebulas.io/ HTTP

更多星云 APIs : RPC

作出贡献

可随意加入星云主网。如果你发现了问题,请 提交Bug 或者 提交改动 来帮助社区。

加入星云测试网
Introduction

我们发布了Nebulas Testnet。它模拟了星云网络和NVM,并允许开发人员与星云交互而无需支付gas费用。

编译

需要首先构建Nebulas Mainnet的可执行文件和依赖库。 以下重点介绍几个重要模块:

  • NEB: 星云主网的主进程。 NEBNBRE运行于独立进程中,两者通过IPC进行通信。

有关编译的详细信息,请参阅 tutorials.

配置文件

testnet 配置文件在 testnet/conf testnet 分支下, 包括

genesis.conf

关于genesis块的所有可配置信息都在genesis.conf中定义,包括

  • meta.chain_id: chain identity
  • consensus.dpos.dynasty: the initial dynasty of validators
  • token_distribution: the initial allocation of tokens
注意: 不要修改 genesis.conf.
config.conf

有关运行时的所有可配置信息都在config.conf中定义。

请查看 template.conf 查找有关运行时配置的更多详细信息。

Tips: 官方节点信息如下,
seed:["/ip4/47.92.203.173/tcp/8680/ipfs/QmfSJ7JUnCEDP6LFyKkBUbpuDMETPbqMVZvPQy4keeyBDP","/ip4/47.89.180.5/tcp/8680/ipfs/QmTmnd5KXm4UFUquAJEGdrwj1cbJCHsTfPWAp5aKrKoRJK"]
Miner config

Nodes can participate in mining and share rewards after signing up for mining. The miner node needs to turn on the mine switch and configure both the miner address and reward address(coinbase).

miner config example:

chain {
  # testnet chainID
  chain_id: 1001
  # testnet datadir, should be different with private chain
  datadir: "testnet/data.db"
  keydir: "keydir"
  # testnet genesis.conf
  genesis: "testnet/conf/genesis.conf"
  # testnet dynasty.conf
  dynasty: "testnet/conf/dynasty.conf"
  
  # start mine
  start_mine: true
  # receive the mining award, must change to your address
  coinbase: "n1XkoVVjswb5Gek3rRufqjKNpwrDdsnQ7Hq"
  # block signature address, needs to be placed in the node's configuration `keydir`. Also make sure that the address is the node address at the time of registration
  miner: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE"
  # 
  passphrase: "passphrase"

  signature_ciphers: ["ECC_SECP256K1"]
}
Synchronization

Since Nebulas testnet is running there for certain period of time, it will take quite some time to sync all the testnet data from scratch.

For developers‘ convenience, we provided a offline data package, which already includes the data of more than 1.2 million blocks, you can download the package directly by following either link below (choose whichever is faster for you):

Please note that, the data package should be put under the same path of “datadir“ as specified in your config.conf file.
API 列表

Test Endpoint:

API URL Protocol
RESTful https://testnet.nebulas.io/ HTTP

更多的星云 APIs : RPC.

申领测试NAS币

每封电子邮件每天都可以获得一些NAS币 点击这里

作出贡献

可随意加入Nebulas Testnet。 如果你确实发现了什么问题,请 submit a issue 或者 submit a pull request 告诉我们,我们会尽快将您的姓名和网址添加到此页面。

配置

There are four types of configuration files in Nebulas.

  • Normal node.
  • Miner node.(Miner - related configuration is increased relative to normal nodes)
  • Super node.(Some connection limits are higher than normal nodes)
  • Sign node. (Do not synchronize information with any node, only do signature and unlock)
Normal node
network {
  seed: ["/ip4/13.251.33.39/tcp/8680/ipfs/QmVm5CECJdPAHmzJWN2X7tP335L5LguGb9QLQ78riA9gw3"]
  listen: ["0.0.0.0:8680"]
  private_key: "conf/networkkey"
}

chain {
  chain_id:1
  datadir: "data.db"
  keydir: "keydir"
  genesis: "conf/genesis.conf"
  signature_ciphers: ["ECC_SECP256K1"]
}

rpc {
    rpc_listen: ["0.0.0.0:8784"]
    http_listen: ["0.0.0.0:8785"]
    http_module: ["api","admin"]
    connection_limits:200
    http_limits:200
}

app {
    log_level: "debug"
    log_file: "logs"
    enable_crash_report: true
}

stats {
    enable_metrics: false
}
Miner node
network {
  seed: ["/ip4/13.251.33.39/tcp/8680/ipfs/QmVm5CECJdPAHmzJWN2X7tP335L5LguGb9QLQ78riA9gw3"]
  listen: ["0.0.0.0:8680"]
  private_key: "conf/networkkey"
}

chain {
  chain_id: 1
  datadir: "data.db"
  keydir: "keydir"
  genesis: "conf/genesis.conf"
  coinbase: "n1EzGmFsVepKduN1U5QFyhLqpzFvM9sRSmG"
  signature_ciphers: ["ECC_SECP256K1"]
  start_mine:true
  miner: "n1PxjEu9sa2nvk9SjSGtJA91nthogZ1FhgY"
  remote_sign_server: "127.0.0.1:8694"
  enable_remote_sign_server: true
}

rpc {
    rpc_listen: ["127.0.0.1:8684"]
    http_listen: ["0.0.0.0:8685"]
    http_module: ["api","admin"]
    connection_limits:200
    http_limits:200
}

app {
    log_level: "debug"
    log_file: "logs"
    enable_crash_report: true
}

stats {
    enable_metrics: false
}
Super node
network {
  seed: ["/ip4/13.251.33.39/tcp/8680/ipfs/QmVm5CECJdPAHmzJWN2X7tP335L5LguGb9QLQ78riA9gw3"]
  listen: ["0.0.0.0:8680"]
  private_key: "conf/networkkey"
  stream_limits: 500
  reserved_stream_limits: 50
}

chain {
  chain_id:1
  datadir: "data.db"
  keydir: "keydir"
  genesis: "conf/genesis.conf"
  signature_ciphers: ["ECC_SECP256K1"]
}

rpc {
    rpc_listen: ["0.0.0.0:8684"]
    http_listen: ["0.0.0.0:8685"]
    http_module: ["api"]
    connection_limits:500
    http_limits:500
    http_cors: ["*"]
}

app {
    log_level: "debug"
    log_file: "logs"
    enable_crash_report: true
    pprof:{
        http_listen: "0.0.0.0:8888"
    }
}

stats {
    enable_metrics: false
}
Sign node
network {
  listen: ["0.0.0.0:8680"]
  private_key: "conf/networkkey"
}

chain {
  chain_id:0
  datadir: "data.db"
  keydir: "keydir"
  genesis: "conf/genesis.conf"
  signature_ciphers: ["ECC_SECP256K1"]
}

rpc {
    rpc_listen: ["0.0.0.0:8684"]
    http_listen: ["127.0.0.1:8685"]
    http_module: ["admin"]
    connection_limits:200
    http_limits:200
}

app {
    log_level: "debug"
    log_file: "logs"
    enable_crash_report: true
    pprof:{
        http_listen: "127.0.0.1:8888"
    }
}

stats {
    enable_metrics: false
}
星云节点环境
Introduction

We are glad to release Nebulas Mainnet here. Please join and enjoy Nebulas Mainnet.

Hardware configuration

The nodes of the nebulas have certain requirements for machine performance. We recommend the performance of the machine has the following requirements:

CPU: >= 4cores(recommand 8 cores)
RAM: >= 16G
Disk: >= 600G SSD
Environment

Node Installation Tutorial - review the Nebulas Technical Documentation: Nebulas 101 - 01 Compile Installation.

It’s recommended to build and deploy nodes via docker:

sudo docker-compose build

sudo docker-compose up -d
System: Ubuntu 18.04(recommand), other Linux is ok.
NTP: Ensure machine time synchronization
NTP

Install the NTP service to keep system time in sync.

Ubuntu install steps:

#install
sudo apt install ntp
#start ntp service
sudo service ntp restart
# check ntp status
ntpq -p

Centos install steps:

#install
sudo yum install ntp
#start ntp service
sudo service ntp restart
# check ntp status
ntpq -p
Contribution

Feel free to join Nebulas Mainnet. If you did find something wrong, please submit a issue or submit a pull request to let us know, we will add your name and url to this page soon.

教程

如果你是开发者,想在星云开发上进行开发,希望本篇教程能对你有所帮助。你也可以访问 开发者页面 查看各类工具。

Go-Nebulas

教程 (星云 101):

01 编译安装星云链

星云链的项目代码已经发布了几个版本,经过测试可以在本地运行,大家可以下载星云链源代码在本地编译私有链。

想了解星云链的同学可以阅读星云链非技术白皮书

对技术感兴趣的同学可以看星云链技术白皮书和星云链Github代码

星云链现阶段只能在Mac和Linux上运行,后续会推出windows版本。
Golang环境搭建
Components Description
Golang The Go Programming Language, version >= 1.12

我们会分别介绍Mac OSX和Linux两种系统下golang环境的搭建。

Mac OSX

在Mac OSX里,我们推荐使用Homebrew来安装Golang.

# 安装
brew install go
Linux
# download
wget https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz

# extract
tar -C /usr/local -xzf go1.14.1.linux-amd64.tar.gz

# environment variables
export PATH=$PATH:/usr/local/go/bin
编译星云链
下载源码

可以使用如下指令直接下载最新版本的星云链源码。

# 进入工作目录
cd /path/to/workspace

# 下载源码
git clone https://github.com/nebulasio/go-nebulas.git

# 进入项目目录
cd go-nebulas

# 切换到最稳定的master分支
git checkout master
安装rocksdb依赖库

同样我们会介绍如何在MAC OSX和Linux正确安装RocksDB。

Mac OSX

可以直接通过Homebrew安装,运行brew install rocksdb

Linux

在linux环境下,由于ubuntu和centos系统的用户偏多,我们将分别介绍这两个主流环境下RocksDB的安装。

Ubuntu

  • 首先安装RocksDB所需依赖库

      apt-get update
      apt-get -y install build-essential libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
    
  • 下载源码并编译安装

      git clone https://github.com/facebook/rocksdb.git
      cd rocksdb 
       git checkout v5.18.3
       sudo make install-shared -j8
       sudo ldconfig
    

Centos

  • 首先安装RocksDB所需依赖库

      yum -y install epel-release && yum -y update
      yum -y install gflags-devel snappy-devel zlib-devel bzip2-devel gcc-c++  libstdc++-devel
    
  • 下载源码并编译安装

      git clone https://github.com/facebook/rocksdb.git
      cd rocksdb 
       git checkout v5.18.3
       sudo make install-shared -j8
       sudo ldconfig
    
安装依赖库

星云虚拟机目前依赖于Chrome的V8引擎,为了大家使用方便,我们已经为Mac OSX和Linux编译好了V8的动态库。运行如下指令就可以完成安装。

cd /path/to/workspace
source setup.sh
编译可执行文件

完成所有上述依赖库的安装后,现在我们可以进入Go-Nebulas根目录编译星云链的可执行文件了。

cd /path/to/workspace
make build

编译成功后,将会看到如下信息。

_images/101-01-make-build.pngmake build

运行星云链
创世区块

在启动一个新的星云链前,我们必须定义创世区块的配置文件。

创世区块配置
meta {
  # 每条链的唯一标识
  # 每个区块和交易只会属于一条唯一的链,保证安全性
  chain_id: 1
}

consensus {
  # pod共识中,21个人组成一个朝代
  # 每隔一段时间都会切换朝代,每个朝代内,21个矿工轮流出块
  dpos {
    # 初始朝代,包含21个初始矿工地址
    dynasty: [
      [ miner address ],
      ...
    ]
  }
}

# 预分配的代币
token_distribution [
  {
    address: [ allocation address ]
    value: [ amount of allocation tokens ]
  },
  ...
]

可以在根目录conf/default/genesis.conf下,找到一个完整的创世区块配置实例。

配置文件

在我们启动一个星云节点前,我们需要定义好该节点的配置文件。

星云节点配置文件
# 网络配置
network {
  # 对于全网第一个节点,不需要配置seed
  # 否则,其他节点启动时需要配置seed,seed节点将会把网络中其他节点的路由信息同步给刚启动的节点
  # 可以配置多个seed, ["...", "..."]
  seed: ["/ip4/127.0.0.1/tcp/8680/ipfs/QmP7HDFcYmJL12Ez4ZNVCKjKedfE7f48f1LAkUc3Whz4jP"]

  # 节点监听网络消息端口,可以配置多个
  listen: ["0.0.0.0:8680"]

  # 网络私钥,用于确认身份节点
  # private_key: "conf/network/id_ed25519"
}

# 链配置
chain {
  # 链的唯一标识
  chain_id: 1

  # 数据存储地址
  datadir: "data.db"

  # 账户keystore文件存储地址
  keydir: "keydir"

  # 创世区块配置
  genesis: "conf/default/genesis.conf"

  # 签名算法,请勿修改
  signature_ciphers: ["ECC_SECP256K1"]

  # 矿工地址,矿工的keystore文件需要放置在配置的keydir下
  miner: "n1XkoVVjswb5Gek3rRufqjKNpwrDdsnQ7Hq"

  # Coinbase地址,该地址用于接收矿工的挖矿奖励,可以和矿工地址一致
  # 该地址的keystore无需暴露,不用放置在配置的keydir下
  coinbase: "n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE"

  # 矿工地址的密码
  passphrase: "passphrase"
}

# API配置
rpc {
    # GRPC服务端口
    rpc_listen: ["127.0.0.1:8684"]

    # HTTP服务端口
    http_listen: ["127.0.0.1:8685"]

    # 开放的API模块
    # API模块包含所有和用户私钥无关的接口
    # Admin模块包含所有和用户私钥相关的接口,需要慎重考虑该模块的访问权限
    http_module: ["api", "admin"]
}

# 日志配置
app {
    # 日志级别: 支持[debug, info, warn, error, fatal]
    log_level: "info"

    # 日志存放位置
    log_file: "logs"

    # 是否打开crash report服务
    enable_crash_report: false
}

# 监控服务配置
stats {
    # 是否打开监控服务
    enable_metrics: false

    # 监控服务将数据上传到Influxdb
    # 配置Influxdb的访问信息
    influxdb: {
        host: "http://localhost:8086"
        db: "nebulas"
        user: "admin"
        password: "admin"
    }
}

A lot of examples can be found in $GOPATH/src/github.com/nebulasio/go-nebulas/conf/

启动星云链
此时启动的星云链是本地的私有链,和官方的测试网和主网没有任何相互关联

启动你的第一个星云节点。

cd $GOPATH/src/github.com/nebulasio/go-nebulas
./neb -c conf/default/config.conf

启动成功的话,将会看到如下信息,有Started Neblet的日志输出。

_images/101-01-seed-node-start.pngseed node start

默认情况下,使用配置文件conf/default/config.conf启动的节点不是矿工节点。

接下来,启动你的第一个矿工节点,它的seed节点即我们刚刚启动的第一个节点。

cd $GOPATH/src/github.com/nebulasio/go-nebulas
./neb -c conf/example/miner.conf

在这个节点启动后,你会先看到如下信息,表示当前节点正在找种子节点同步。

_images/101-01-start-sync.pngstart sync

等待一会儿,将会看到如下信息,表示当前节点已经连上了seed节点完成了同步。

_images/101-01-finish-sync.pngfinish sync

再等待几分钟,你会看到如下信息,表示当前矿工节点挖出了第一个区块。

_images/101-01-mint.pngfinish sync

提示: 目前的PoD共识算法,会有21个节点轮流出块。由于我们只启动了21个矿工节点中的一个矿工节点,所以每隔15*21s才出一个块。你可以启动更多的矿工节点,填补的空缺。但是需要注意,多个节点间的端口号不要相互冲突了。
02 在星云链上发送交易

Nebulas提供了三种方式去发送我们的交易:

  1. 签名 & 发送
  2. 密码 & 发送
  3. 解锁 & 发送

下面我们分别介绍如何通过以上三种方式在nebulas中发送一笔交易,并验证交易是否成功。

准备账户

在星云链上,每个地址表示一个唯一的账户,一一对应。

在发送交易前,我们需要准备两个账户:一个账户用来发送代币 (称为“from“) 和另一个账户来接受代币 (称为“to“).

发送者账户

在这里,我们将会使用配置文件conf/default/genesis.conf中预分配过代币的账户中选择一个作为发送者账户,默认选择n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE

接受者账户

我们使用如下指令创建一个全新的账户来做接受者,请记住输入的密码。

$ ./neb account new
Your new account is locked with a passphrase. Please give a passphrase. Do not forget this passphrase.
Passphrase:
Repeat passphrase:
Address: n1SQe5d1NKHYFMKtJ5sNHPsSPVavGzW71Wy
提示:你创建的新账户和上面可能不一样,请以你创建的账户做为接受者继续接下来的实验。

新账户的keystore文件将会被放置在$GOPATH/src/github.com/nebulasio/go-nebulas/keydir/内。

启动私有链

我们将在本地搭建一个私有链来作为本教程的测试环境。

启动种子节点

首先,我们启动本地私有链的第一个节点,它可以作为其他节点的种子节点。

./neb -c conf/default/config.conf
启动矿工节点

接着,我们启动一个矿工节点接入本地私有链,这个节点之后将会产生新的区块。

./neb -c conf/example/miner.conf

多久会生成一个新的区块?

在星云链上, 在贡献度证明(Proof-of-Devotion, 技术白皮书中有详细描述)中,总共有21个矿工,每个矿工会轮流每15s出一个新区块。

在我们目前的测试环境中,由于我们只启动了21个矿工中的一个,所以需要等待15*21s才会出一个新区块。

一旦一个新区块被挖出,挖块奖励将会被自动发送到当前矿工配置的Coinbase账户中,在conf/example/miner.conf里,该账户就是n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE.

星云链交互

星云链提供给开发者HTTP API, RPC API和CLI来和运行中的星云节点交互。在教程中,我们将会基于HTTP API(API Module | Admin Module)来介绍三种发送交易的方法。

提示:星云链的HTTP服务默认端口号为8685。

首先,在发送新交易前,我们检查下发送者账户的状态。

检查账户状态

每个交易如果需要上链,都需要给矿工缴纳一部分手续费,所以发送者账户中需要有一部分钱才能成功发送交易。一般一个普通转账交易,手续费在0.000000002NAS左右,非常少。

我们可以通过API Module中的/v1/user/accountstate接口来获取发送者账户n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE的账户信息,检查下是否有足够的钱支付上链手续费。

> curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE"}'

{
    "result": {
        "balance": "5000000000000000000000000",
        "nonce": "0",
        "type": 87,
        "height":"1",
        "pending":"0"
    }
}

> 提示:`Type`用于标记账户类型。88表示该账户为智能合约账户,部署一个合约之后,就可以得到一个合约账户。87表示非合约账户,我们通过`./neb account new`创建的账户就是非合约账户,用户存储链上资产。
>
> 提示:`Nonce`用于标记账户发起的所有交易的顺序。同一个账户,每发起一个新的交易,`Nonce`就加一,初始为0,第一个交易的`Nonce`为1。
>
> 提示:`Height`表示Api调用时区块链当前高度。
>
> 提示:`Pending`表示当前交易池中pending的该地址的交易数目。

如我们所见,发送者账户在预分配后拥有5000000000000000000000000\(5 \* 10\^24\)个代币,1个NAS是1000000000000000000(10\^18)个代币,用于支付交易上链的手续费绰绰有余。

然后我们检查接受者账户的状态。

```bash
> curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"your_address"}'

{
    "result": {
        "balance": "0",
        "nonce": "0",
        "type": 87,
        "height":"1",
        "pending":"0"
    }
}

如我们期望的那样,新账户没有任何代币。

发送交易

接下来,我们将介绍星云链上三种发送交易的方式。

签名 & 发送

使用这种方式,我们可以在离线环境下先使用私钥签名好交易,然后把签好名的交易在联网的机器上发出。这是最安全的发送交易的方式,私钥可以完全离线保存,不触网。Web-Wallet正是基于Neb.js采用这种方法发送的交易。

首先,我们使用Admin Module中的v1/admin/sign接口给准备发的交易签名,得到交易的二进制数据。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/sign -d '{"transaction":{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":1,"gasPrice":"1000000","gasLimit":"2000000"}, "passphrase":"passphrase"}'

{"result":{"data":"CiAbjMP5dyVsTWILfXL1MbwZ8Q6xOgX/JKinks1dpToSdxIaGVcH+WT/SVMkY18ix7SG4F1+Z8evXJoA35caGhlXbip8PupTNxwV4SRM87r798jXWADXpWngIhAAAAAAAAAAAA3gtrOnZAAAKAEwuKuC1wU6CAoGYmluYXJ5QGRKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAAehIBYAWJBVVuRHWSNY1e3bigbVKd9i6ci4f1LruDC7AUtXDLirHlsmTDZXqjSMGLio1ziTmxYJiLj+Jht5RoZxFKqFncOIQA="}}
提示:在发送交易时,对于同一个账户,只有当他Nonce为N的交易上链后,Nonce为N+1的交易才能上链,有严格的顺序,Nonce必须严格加1。可以通过GetAccountState接口查看最新的Nonce。

然后,我们将签好名的交易原始数据提交到本地私有链里的星云节点。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/rawtransaction -d '{"data":"CiAbjMP5dyVsTWILfXL1MbwZ8Q6xOgX/JKinks1dpToSdxIaGVcH+WT/SVMkY18ix7SG4F1+Z8evXJoA35caGhlXbip8PupTNxwV4SRM87r798jXWADXpWngIhAAAAAAAAAAAA3gtrOnZAAAKAEwuKuC1wU6CAoGYmluYXJ5QGRKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAAehIBYAWJBVVuRHWSNY1e3bigbVKd9i6ci4f1LruDC7AUtXDLirHlsmTDZXqjSMGLio1ziTmxYJiLj+Jht5RoZxFKqFncOIQA="}'

{"result":{"txhash":"1b8cc3f977256c4d620b7d72f531bc19f10eb13a05ff24a8a792cd5da53a1277","contract_address":""}}
密码 & 发送

如果你信任一个星云节点帮你保存keystore文件,你可以使用第二种方法发送交易。

首先,上传你的keystore文件到你信任的星云节点的keydir文件夹下。如果在节点在本地,可以使用如下指令。

cp /path/to/keystore.json /path/to/keydir/

然后,我们发送交易的同时,带上我们keystore的密码,在被信任的节点使用SendTransactionWithPassphrase接口上一次性完成签名和发送过程。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -d '{"transaction":{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":2,"gasPrice":"20000000000","gasLimit":"2000000"},"passphrase":"passphrase"}'

{"result":{"txhash":"3cdd38a66c8f399e2f28134e0eb556b292e19d48439f6afde384ca9b60c27010","contract_address":""}}
提示:因为我们在之前使用n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE发送了一个Nonce为1的交易,所以这里新的交易的Nonce应该增加1,变成2再提交。
解锁 & 发送

这是最危险的发送交易的方法。除非你完全信任一个星云节点,否则不要使用这种方法来发送交易。

首先,上传你的keystore文件到你信任的星云节点的keydir文件夹下。如果在节点在本地,可以使用如下指令。

cp /path/to/keystore.json /path/to/keydir/

然后,使用你的keystore文件的密码,在指定的时间范围来在被信任的节点上使用Unlock接口解锁账户。时间单位为纳秒,300000000000为300s。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/unlock -d '{"address":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","passphrase":"passphrase","duration":"300000000000"}'

{"result":{"result":true}}

一旦一个账户在节点上被解锁,任何可以访问该机器SendTransaction接口的人,都可以直接使用该账户的身份发送交易。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transaction -d '{"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":3,"gasPrice":"20000000000","gasLimit":"2000000"}'

{"result":{"txhash":"8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf","contract_address":""}}
交易收据

不论使用的哪一种方法发送交易,我们都会得到两个返回值,txhashcontract_address。其中txhash为交易hash,是一个交易的唯一标识。如果当前交易是一个部署合约的交易,contract_address将会是合约地址,调用合约时都会使用这个地址,是合约的唯一标识。我们将在编写并运行智能合约中介绍如何发送部署合约的交易。

使用txhash我们可以查看交易收据,知道当前交易的状态。

> curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/getTransactionReceipt -d '{"hash":"8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf"}'

{"result":{"hash":"8d69dea784f0edfb2ee678c464d99e155bca04b3d7e6cdba6c5c189f731110cf","chainId":100,"from":"n1FF1nz6tarkDVwWQkMnnwFPuPKUaQTdptE","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","value":"1000000000000000000","nonce":"3","timestamp":"1524667888","type":"binary","data":null,"gas_price":"20000000000","gas_limit":"2000000","contract_address":"","status":1,"gas_used":"20000"}}

这里的status可能有三种状态值,0,1和2。

  • 0: 交易失败. 表示当前交易已经上链,但是执行失败了。可能是因为部署合约或者调用合约参数错误。
  • 1: 交易成功. 表示当前交易已经上链,而且执行成功了。
  • 2: 交易待定. 表示当前交易还没有上链。可能是因为当前交易还没有被打包;如果长时间处于当前状态,可能是因为当前交易的发送者账户的余额不够支付上链手续费。
复查接受者账户余额

我们复查一下接受者账户上的钱是否已经到账了。

> curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5"}'

{"result":{"balance":"3000000000000000000","nonce":"0","type":87,"height":"15","pending":"0"}}

我们用三种方式分别发送了一笔转账,每笔转一个NAS,所以这里看到接受者账户中已经有了3个NAS,即3000000000000000000个代币。

03 编写并运行智能合约

在这篇教程中我们会学习怎样在Nebulas中编写、部署并执行智能合约。

准备工作

在进入智能合约之前,先温习下先前学过的内容:

  1. 安装、编译并启动neb应用
  2. 创建钱包地址,设置coinbase,并开始挖矿
  3. 查询neb节点信息、钱包地址余额等
  4. 发送转账交易,并验证交易是否成功

如果对上述的内容有疑惑的同学可以重新去学习前面的章节,接下来我们会通过下面的步骤来学习和使用智能合约:

  1. 编写智能合约
  2. 部署智能合约
  3. 调用智能合约,验证合约执行结果
编写智能合约

跟以太坊类似,Nebulas实现了NVM虚拟机来运行智能合约,NVM的实现使用了JavaScript V8引擎,所以当前的开发版,我们可以使用JavaScript、TypeScript来编写智能合约。

编写智能合约的简要规范:

  1. 智能合约代码必须是一个Prototype的对象;
  2. 智能合约代码必须有一个init()的方法,这个方法只会在部署的时候被执行一次;
  3. 智能合约里面的私有方法是以_开头的方法,私有方法不能被外部直接调用;

下面我们使用JavaScript来编写第一个智能合约:银行保险柜。 这个智能合约需要实现以下功能:

  1. 用户可以向这个银行保险柜存钱。
  2. 用户可以从这个银行保险柜取钱。
  3. 用户可以查询银行保险柜中的余额。

智能合约示例:

'use strict';

var DepositeContent = function (text) {
  if (text) {
    var o = JSON.parse(text);
    this.balance = new BigNumber(o.balance);
    this.expiryHeight = new BigNumber(o.expiryHeight);
  } else {
    this.balance = new BigNumber(0);
    this.expiryHeight = new BigNumber(0);
  }
};

DepositeContent.prototype = {
  toString: function () {
    return JSON.stringify(this);
  }
};

var BankVaultContract = function () {
  LocalContractStorage.defineMapProperty(this, "bankVault", {
    parse: function (text) {
      return new DepositeContent(text);
    },
    stringify: function (o) {
      return o.toString();
    }
  });
};

// save value to contract, only after height of block, users can takeout
BankVaultContract.prototype = {
  init: function () {
    //TODO:
  },

  save: function (height) {
    var from = Blockchain.transaction.from;
    var value = Blockchain.transaction.value;
    var bk_height = new BigNumber(Blockchain.block.height);

    var orig_deposit = this.bankVault.get(from);
    if (orig_deposit) {
      value = value.plus(orig_deposit.balance);
    }

    var deposit = new DepositeContent();
    deposit.balance = value;
    deposit.expiryHeight = bk_height.plus(height);

    this.bankVault.put(from, deposit);
  },

  takeout: function (value) {
    var from = Blockchain.transaction.from;
    var bk_height = new BigNumber(Blockchain.block.height);
    var amount = new BigNumber(value);

    var deposit = this.bankVault.get(from);
    if (!deposit) {
      throw new Error("No deposit before.");
    }

    if (bk_height.lt(deposit.expiryHeight)) {
      throw new Error("Can not takeout before expiryHeight.");
    }

    if (amount.gt(deposit.balance)) {
      throw new Error("Insufficient balance.");
    }

    var result = Blockchain.transfer(from, amount);
    if (!result) {
      throw new Error("transfer failed.");
    }
    Event.Trigger("BankVault", {
      Transfer: {
        from: Blockchain.transaction.to,
        to: from,
        value: amount.toString()
      }
    });

    deposit.balance = deposit.balance.sub(amount);
    this.bankVault.put(from, deposit);
  },
  balanceOf: function () {
    var from = Blockchain.transaction.from;
    return this.bankVault.get(from);
  },
  verifyAddress: function (address) {
    // 1-valid, 0-invalid
    var result = Blockchain.verifyAddress(address);
    return {
      valid: result == 0 ? false : true
    };
  }
};
module.exports = BankVaultContract;

上面智能合约的示例可以看到,BankVaultContract是一个prototype对象,这个对象有一个init()方法,满足了我们说的编写智能合约最基础的规范。

BankVaultContract实现了另外两个方法:

  • save(): 用户可以通过调用save()方法向银行保险柜存钱;
  • takeout(): 用户可以通过调用takeout()方法向银行保险柜取钱;
  • balanceOf(): 用户可以通过调用balanceOf()方法向银行保险柜查询余额;

上面的合约代码用到了内置的Blockchain对象和内置的BigNumber()方法,下面我们来逐行拆解分析合约代码:

save():

// Deposit the amount into the safe

save: function (height) {
  var from = Blockchain.transaction.from;
  var value = Blockchain.transaction.value;
  var bk_height = new BigNumber(Blockchain.block.height);

  var orig_deposit = this.bankVault.get(from);
  if (orig_deposit) {
    value = value.plus(orig_deposit.balance);
  }
  var deposit = new DepositeContent();
  deposit.balance = value;
  deposit.expiryHeight = bk_height.plus(height);

  this.bankVault.put(from, deposit);
},

takeout():

takeout: function (value) {
  var from = Blockchain.transaction.from;
  var bk_height = new BigNumber(Blockchain.block.height);
  var amount = new BigNumber(value);

  var deposit = this.bankVault.get(from);
  if (!deposit) {
    throw new Error("No deposit before.");
  }

  if (bk_height.lt(deposit.expiryHeight)) {
    throw new Error("Can not takeout before expiryHeight.");
  }

  if (amount.gt(deposit.balance)) {
    throw new Error("Insufficient balance.");
  }

  var result = Blockchain.transfer(from, amount);
  if (!result) {
    throw new Error("transfer failed.");
  }
  Event.Trigger("BankVault", {
    Transfer: {
      from: Blockchain.transaction.to,
      to: from,
      value: amount.toString()
    }
  });

  deposit.balance = deposit.balance.sub(amount);
  this.bankVault.put(from, deposit);
},
部署智能合约

上面介绍了在Nebulas中怎么去编写一个智能合约,现在我们需要把智能合约部署到链上。在Nebulas中部署一个智能合约其实也是发送一个transaction来实现,前面有介绍过用户如何在Nebulas中进行转账交易,由于是本地测试,我们直接使用解锁 & 发送的方式来发送交易。

首先,我们在conf/default/genesis.conf中预分配过代币的账户里,选择账户n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so作为本章的发送者账号,并检查该账户的状态。

> curl -i -H Accept:application/json -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so"}'

{"result":{"balance":"5000000000000000000000000","nonce":"0","type":87}}

该账户有足够的钱来支付手续费,接下来,我们解锁发送者账户n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so,解锁12小时。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/unlock -d '{"address":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so","passphrase":"passphrase","duration":"43200000000000"}'

{"result":{"result":true}}

然后,我们发送部署BankVault合约的交易。

> curl -i -H 'Accept: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -H 'Content-Type: application/json' -d '{"transaction": {"from":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so","to":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so", "value":"0","nonce":1,"gasPrice":"20000000000","gasLimit":"2000000","contract":{"source":"\"use strict\";var DepositeContent=function(text){if(text){var o=JSON.parse(text);this.balance=new BigNumber(o.balance);this.expiryHeight=new BigNumber(o.expiryHeight);}else{this.balance=new BigNumber(0);this.expiryHeight=new BigNumber(0);}};DepositeContent.prototype={toString:function(){return JSON.stringify(this);}};var BankVaultContract=function(){LocalContractStorage.defineMapProperty(this,\"bankVault\",{parse:function(text){return new DepositeContent(text);},stringify:function(o){return o.toString();}});};BankVaultContract.prototype={init:function(){},save:function(height){var from=Blockchain.transaction.from;var value=Blockchain.transaction.value;var bk_height=new BigNumber(Blockchain.block.height);var orig_deposit=this.bankVault.get(from);if(orig_deposit){value=value.plus(orig_deposit.balance);} var deposit=new DepositeContent();deposit.balance=value;deposit.expiryHeight=bk_height.plus(height);this.bankVault.put(from,deposit);},takeout:function(value){var from=Blockchain.transaction.from;var bk_height=new BigNumber(Blockchain.block.height);var amount=new BigNumber(value);var deposit=this.bankVault.get(from);if(!deposit){throw new Error(\"No deposit before.\");} if(bk_height.lt(deposit.expiryHeight)){throw new Error(\"Can not takeout before expiryHeight.\");} if(amount.gt(deposit.balance)){throw new Error(\"Insufficient balance.\");} var result=Blockchain.transfer(from,amount);if(!result){throw new Error(\"transfer failed.\");} Event.Trigger(\"BankVault\",{Transfer:{from:Blockchain.transaction.to,to:from,value:amount.toString()}});deposit.balance=deposit.balance.sub(amount);this.bankVault.put(from,deposit);},balanceOf:function(){var from=Blockchain.transaction.from;return this.bankVault.get(from);},verifyAddress:function(address){var result=Blockchain.verifyAddress(address);return{valid:result==0?false:true};}};module.exports=BankVaultContract;","sourceType":"js", "args":""}}, "passphrase": "passphrase"}'

{"result":{"txhash":"aaebb86d15ca30b86834efb600f82cbcaf2d7aaffbe4f2c8e70de53cbed17889","contract_address":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM"}}
  • from: 合约创建者账户地址
  • to: 和from一致,同为合约创建者地址
  • value:部署合约时为"0"
  • nonce: 比创建者当前的nonce多1,可以通过GetAccountState获取创建前当前nonce
  • gasPrice:部署智能合约用到的gasPrice,可以通过GetGasPrice获取,或者使用默认值:"1000000"
  • gasLimit: 部署合约的gasLimit,通过EstimateGas可以获取部署合约的gas消耗,不能使用默认值,也可以设置一个较大值,执行时以实际使用计算。
  • contract: 合约信息,部署合约时传入的参数
    • source: 合约代码
    • sourceType: 合约代码类型,支持jsts(对应javaScript和typeScript代码)
    • args: 合约初始化方法参数,无参数为空字符串,有参数时为JSON数组

部署智能合约的返回值是transaction的hash地址txhash和合约的部署地址contract_address。 得到返回值并不能保证合约已经部署成功,因为发送交易是一个异步的过程,需要经过矿工打包,正如之前的转账交易一样,转账并不能实时到账,依赖矿工打包的速度,所以需要等待一段时间,然后可以通过查询合约地址的信息或者调用智能合约来验证合约是否部署成功。

验证合约是否部署成功

在部署智能合约的时候得到了transaction的hash地址txhash,我们可以很方便的使用console控制台查询transaction的hash信息来验证合约是否部署成功。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getTransactionReceipt -d '{"hash":"aaebb86d15ca30b86834efb600f82cbcaf2d7aaffbe4f2c8e70de53cbed17889"}'

{"result":{"hash":"aaebb86d15ca30b86834efb600f82cbcaf2d7aaffbe4f2c8e70de53cbed17889","chainId":100,"from":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so","to":"n1H4MYms9F55ehcvygwWE71J8tJC4CRr2so","value":"0","nonce":"1","timestamp":"1524711841","type":"deploy","data":"eyJTb3VyY2VUeXBlIjoianMiLCJTb3VyY2UiOiJcInVzZSBzdHJpY3RcIjt2YXIgRGVwb3NpdGVDb250ZW50PWZ1bmN0aW9uKHRleHQpe2lmKHRleHQpe3ZhciBvPUpTT04ucGFyc2UodGV4dCk7dGhpcy5iYWxhbmNlPW5ldyBCaWdOdW1iZXIoby5iYWxhbmNlKTt0aGlzLmV4cGlyeUhlaWdodD1uZXcgQmlnTnVtYmVyKG8uZXhwaXJ5SGVpZ2h0KTt9ZWxzZXt0aGlzLmJhbGFuY2U9bmV3IEJpZ051bWJlcigwKTt0aGlzLmV4cGlyeUhlaWdodD1uZXcgQmlnTnVtYmVyKDApO319O0RlcG9zaXRlQ29udGVudC5wcm90b3R5cGU9e3RvU3RyaW5nOmZ1bmN0aW9uKCl7cmV0dXJuIEpTT04uc3RyaW5naWZ5KHRoaXMpO319O3ZhciBCYW5rVmF1bHRDb250cmFjdD1mdW5jdGlvbigpe0xvY2FsQ29udHJhY3RTdG9yYWdlLmRlZmluZU1hcFByb3BlcnR5KHRoaXMsXCJiYW5rVmF1bHRcIix7cGFyc2U6ZnVuY3Rpb24odGV4dCl7cmV0dXJuIG5ldyBEZXBvc2l0ZUNvbnRlbnQodGV4dCk7fSxzdHJpbmdpZnk6ZnVuY3Rpb24obyl7cmV0dXJuIG8udG9TdHJpbmcoKTt9fSk7fTtCYW5rVmF1bHRDb250cmFjdC5wcm90b3R5cGU9e2luaXQ6ZnVuY3Rpb24oKXt9LHNhdmU6ZnVuY3Rpb24oaGVpZ2h0KXt2YXIgZnJvbT1CbG9ja2NoYWluLnRyYW5zYWN0aW9uLmZyb207dmFyIHZhbHVlPUJsb2NrY2hhaW4udHJhbnNhY3Rpb24udmFsdWU7dmFyIGJrX2hlaWdodD1uZXcgQmlnTnVtYmVyKEJsb2NrY2hhaW4uYmxvY2suaGVpZ2h0KTt2YXIgb3JpZ19kZXBvc2l0PXRoaXMuYmFua1ZhdWx0LmdldChmcm9tKTtpZihvcmlnX2RlcG9zaXQpe3ZhbHVlPXZhbHVlLnBsdXMob3JpZ19kZXBvc2l0LmJhbGFuY2UpO30gdmFyIGRlcG9zaXQ9bmV3IERlcG9zaXRlQ29udGVudCgpO2RlcG9zaXQuYmFsYW5jZT12YWx1ZTtkZXBvc2l0LmV4cGlyeUhlaWdodD1ia19oZWlnaHQucGx1cyhoZWlnaHQpO3RoaXMuYmFua1ZhdWx0LnB1dChmcm9tLGRlcG9zaXQpO30sdGFrZW91dDpmdW5jdGlvbih2YWx1ZSl7dmFyIGZyb209QmxvY2tjaGFpbi50cmFuc2FjdGlvbi5mcm9tO3ZhciBia19oZWlnaHQ9bmV3IEJpZ051bWJlcihCbG9ja2NoYWluLmJsb2NrLmhlaWdodCk7dmFyIGFtb3VudD1uZXcgQmlnTnVtYmVyKHZhbHVlKTt2YXIgZGVwb3NpdD10aGlzLmJhbmtWYXVsdC5nZXQoZnJvbSk7aWYoIWRlcG9zaXQpe3Rocm93IG5ldyBFcnJvcihcIk5vIGRlcG9zaXQgYmVmb3JlLlwiKTt9IGlmKGJrX2hlaWdodC5sdChkZXBvc2l0LmV4cGlyeUhlaWdodCkpe3Rocm93IG5ldyBFcnJvcihcIkNhbiBub3QgdGFrZW91dCBiZWZvcmUgZXhwaXJ5SGVpZ2h0LlwiKTt9IGlmKGFtb3VudC5ndChkZXBvc2l0LmJhbGFuY2UpKXt0aHJvdyBuZXcgRXJyb3IoXCJJbnN1ZmZpY2llbnQgYmFsYW5jZS5cIik7fSB2YXIgcmVzdWx0PUJsb2NrY2hhaW4udHJhbnNmZXIoZnJvbSxhbW91bnQpO2lmKCFyZXN1bHQpe3Rocm93IG5ldyBFcnJvcihcInRyYW5zZmVyIGZhaWxlZC5cIik7fSBFdmVudC5UcmlnZ2VyKFwiQmFua1ZhdWx0XCIse1RyYW5zZmVyOntmcm9tOkJsb2NrY2hhaW4udHJhbnNhY3Rpb24udG8sdG86ZnJvbSx2YWx1ZTphbW91bnQudG9TdHJpbmcoKX19KTtkZXBvc2l0LmJhbGFuY2U9ZGVwb3NpdC5iYWxhbmNlLnN1YihhbW91bnQpO3RoaXMuYmFua1ZhdWx0LnB1dChmcm9tLGRlcG9zaXQpO30sYmFsYW5jZU9mOmZ1bmN0aW9uKCl7dmFyIGZyb209QmxvY2tjaGFpbi50cmFuc2FjdGlvbi5mcm9tO3JldHVybiB0aGlzLmJhbmtWYXVsdC5nZXQoZnJvbSk7fSx2ZXJpZnlBZGRyZXNzOmZ1bmN0aW9uKGFkZHJlc3Mpe3ZhciByZXN1bHQ9QmxvY2tjaGFpbi52ZXJpZnlBZGRyZXNzKGFkZHJlc3MpO3JldHVybnt2YWxpZDpyZXN1bHQ9PTA/ZmFsc2U6dHJ1ZX07fX07bW9kdWxlLmV4cG9ydHM9QmFua1ZhdWx0Q29udHJhY3Q7IiwiQXJncyI6IiJ9","gas_price":"1000000","gas_limit":"2000000","contract_address":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM","status":1,"gas_used":"22016"}}

如上所示,部署合约的交易的状态变成了1,表示合约部署成功了。

执行智能合约方法

在Nebulas中调用智能合约的方式也很简单,同样是通过发送交易来调用智能合约。

调用智能合约的save方法
> curl -i -H 'Accept: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -H 'Content-Type: application/json' -d '{"transaction":{"from":"n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS","to":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM", "value":"100","nonce":1,"gasPrice":"1000000","gasLimit":"2000000","contract":{"function":"save","args":"[0]"}}, "passphrase": "passphrase"}'

{"result":{"txhash":"5337f1051198b8ac57033fec98c7a55e8a001dbd293021ae92564d7528de3f84","contract_address":""}}
  • from: 用户的账户地址
  • to: 智能合约地址
  • value: 调用save()方法时想要存入智能合约代币数量
  • nonce: 比创建者当前的nonce多1,可以通过GetAccountState获取创建前当前nonce
  • gasPrice:部署智能合约用到的gasPrice,可以通过GetGasPrice获取,或者使用默认值:"1000000"
  • gasLimit: 部署合约的gasLimit,通过EstimateGas可以获取部署合约的gas消耗,不能使用默认值,也可以设置一个较大值,执行时以实际使用计算。
  • contract: 合约信息,部署合约时传入的参数
  • function: 调用合约方法
  • args: 合约方法参数,无参数为空字符串,有参数时为JSON数组

验证智能合约的方法save是否执行成功

由于执行合约方法本质是提交一个交易,所以我们可以通过验证交易是否提交成功来判断合约方法是否执行成功。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getTransactionReceipt -d '{"hash":"5337f1051198b8ac57033fec98c7a55e8a001dbd293021ae92564d7528de3f84"}'

{"result":{"hash":"5337f1051198b8ac57033fec98c7a55e8a001dbd293021ae92564d7528de3f84","chainId":100,"from":"n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS","to":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM","value":"100","nonce":"1","timestamp":"1524712532","type":"call","data":"eyJGdW5jdGlvbiI6InNhdmUiLCJBcmdzIjoiWzBdIn0=","gas_price":"20000000000","gas_limit":"2000000","contract_address":"","status":1,"gas_used":"20361"}}

如上所示,交易状态变为了1,表示执行save方法成功了。

调用智能合约的takeout方法
> curl -i -H 'Accept: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -H 'Content-Type: application/json' -d '{"transaction":{"from":"n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS","to":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM", "value":"100","nonce":1,"gasPrice":"20000000000","gasLimit":"2000000","contract":{"function":"save","args":"[0]"}}, "passphrase": "passphrase"}'

{"result":{"txhash":"5337f1051198b8ac57033fec98c7a55e8a001dbd293021ae92564d7528de3f84","contract_address":""}}

验证智能合约的方法takeout是否执行成功

在上面save方法的执行中,我们在合约n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM中存了100NAS。此时,我们执行takeout函数,从中取出50NAS。合约里应该还有50NAS。我们检测下合约账户的余额来验证takeout方法执行是否成功。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM"}'

{"result":{"balance":"50","nonce":"0","type":88}}

结果和预期相符。

查询智能合约数据

在智能合约中,我们有一些方法并不更改合约的状态,这些方法被设计来帮助我们获取合约数据,它们是只读的。在星云链上,我们在API Module中为用户提供了Call接口来帮助用户来执行这些只读的方法,使用Call接口不会发送交易,也就无需支付上链手续费。

我们调用合约n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM中的balanceOf方法来查询该合约的余额。

> curl -i -H 'Accept: application/json' -X POST http://localhost:8685/v1/user/call -H 'Content-Type: application/json' -d '{"from":"n1LkDi2gGMqPrjYcczUiweyP4RxTB6Go1qS","to":"n1rVLTRxQEXscTgThmbTnn2NqdWFEKwpYUM","value":"0","nonce":3,"gasPrice":"20000000000","gasLimit":"2000000","contract":{"function":"balanceOf","args":""}}'

{"result":{"result":"{\"balance\":\"50\",\"expiryHeight\":\"84\"}","execute_err":"","estimate_gas":"20209"}}
04 智能合约存储区

前面我们介绍了怎么编写智能合约以及怎样在星云链部署和调用智能合约。

今天我们来详细的介绍有关星云链智能合约的存储。星云链智能合约(smart contract)提供了链上数据存储功能。类似于传统的key-value存储系统(eg:redis),可以付费(消耗gas)将数据存储到星云链上。

LocalContractStorage

星云链的智能合约运行环境内置了存储对象LocalContractStorage,可以存储数字,字符串,JavaScript对象,存储数据只能在智能合约内使用,其他合约不能读取存储的内容。

基础用法

LocalContractStorage的简单接口包括set,get,del接口,实现了存储,读取,删除数据功能。存储可以是数字,字符串,对象。

LocalContractStorage存储数据

// 存储数据,数据会被json序列化成字符串保存
LocalContractStorage.put(key, value);
// 或者
LocalContractStorage.set(key, value);

LocalContractStorage读取数据

// 获取数据
LocalContractStorage.get(key);

LocalContractStorage删除数据

// 删除数据, 数据删除后无法读取
LocalContractStorage.del(key);
// 或者
LocalContractStorage.delete(key);

下面是一个具体在合约中使用LocalContractStorage的实例:

'use strict';

var SampleContract = function () {
};

SampleContract.prototype = {
    init: function () {
    },
    set: function (name, value) {
        // 存储字符串
        LocalContractStorage.set("name",name);
        // 存储数字
        LocalContractStorage.set("value", value);
        // 存储对象
        LocalContractStorage.set("obj", {name:name,value:value});
    },
    get: function () {
        var name = LocalContractStorage.get("name");
        console.log("name:"+name)
        var value = LocalContractStorage.get("value");
        console.log("value:"+value)
        var obj = LocalContractStorage.get("obj");
        console.log("obj:"+JSON.stringify(obj))
    },
    del: function () {
        var result = LocalContractStorage.del("name");
        console.log("del result:"+result)
    }
};

module.exports = SampleContract;
高级用法

LocalContractStorage除了基本的set,get,del方法,还提供方法来绑定合约属性。对绑定过的合约属性的读写将直接在LocalContractStorage上读写,而无需调用getset方法。

绑定属性

在绑定一个合约属性时,需要提供对象实例,属性名和序列化方法。

绑定接口

// define a object property named `fieldname` to `obj` with descriptor.
// default descriptor is JSON.parse/JSON.stringify descriptor.
// return this.
defineProperty(obj, fieldName, descriptor);

// define object properties to `obj` from `props`.
// default descriptor is JSON.parse/JSON.stringify descriptor.
// return this.
defineProperties(obj, descriptorMap);

下面是一个在合约中使用LocalContractStorage绑定属性的例子:

'use strict';

var SampleContract = function () {
    // SampleContract的`size`属性为存储属性,对`size`的读写会存储到链上,
    // 此处的`descriptor`设置为null,将使用默认的JSON.stringify()和JSON.parse()
    LocalContractStorage.defineMapProperty(this, "size", null);

    // SampleContract的`value`属性为存储属性,对`value`的读写会存储到链上,
    // 此处的`descriptor`自定义实现,存储时直接转为字符串,读取时获得Bignumber对象
    LocalContractStorage.defineMapProperty(this, "value", {
        stringify: function (obj) {
            return obj.toString();
        },
        parse: function (str) {
            return new BigNumber(str);
        }
    });
    // SampleContract的多个属性批量设置为存储属性,对应的descriptor默认使用JSON序列化
    LocalContractStorage.defineProperties(this, {
        name: null,
        count: null
    });
};
module.exports = SampleContract;

然后,我们可以如下在合约里直接读写这些属性。

SampleContract.prototype = {
    // 合约部署时调用,部署后无法二次调用
    init: function (name, count, size, value) {
        // 在部署合约时将数据存储到链上
        this.name = name;
        this.count = count;
        this.size = size;
        this.value = value;
    },
    testStorage: function (balance) {
        // 使用value时会从存储中读取链上数据,并根据descriptor设置自动转换为Bignumber
        var amount = this.value.plus(new BigNumber(2));
        if (amount.lessThan(new BigNumber(balance))) {
            return 0
        }
    }
};
绑定Map属性

LocalContractStorage还提供了对合约中map属性的绑定方法。

下面是一个绑定map属性的例子:

'use strict';

var SampleContract = function () {
    // 为`SampleContract`定义`userMap`的属性集合,数据可以通过`userMap`存储到链上
    LocalContractStorage.defineMapProperty(this, "userMap");

    // 为`SampleContract`定义`userBalanceMap`的属性集合,并且存储和读取序列化方法自定义
    LocalContractStorage.defineMapProperty(this, "userBalanceMap", {
        stringify: function (obj) {
            return obj.toString();
        },
        parse: function (str) {
            return new BigNumber(str);
        }
    });

    // 为`SampleContract`定义多个集合
    LocalContractStorage.defineMapProperties(this,{
        key1Map: null,
        key2Map: null
    });
};

SampleContract.prototype = {
    init: function () {
    },
    testStorage: function () {
        // 将数据存储到userMap中,并序列化到链上
        this.userMap.set("robin","1");
        // 将数据存储到userBalanceMap中,使用自定义序列化函数,保存到链上
        this.userBalanceMap.set("robin",new BigNumber(1));
    },
    testRead: function () {
        //读取存储数据
        var balance = this.userBalanceMap.get("robin");
        this.key1Map.set("robin", balance.toString());
        this.key2Map.set("robin", balance.toString());
    }
};

module.exports = SampleContract;
遍历map数据

Map数据遍历

在智能合约中如果需要遍历map集合,可以采用如下方式:定义两个map,分别是arrayMap,dataMap,arrayMap采用严格递增的计数器作为key,dataMap采用data的key作为key,详细参见set方法。遍历实现参见forEach,先遍历arrayMap,得到dataKey,再对dataMap遍历。Tip:由于Map遍历性能开销比较大,不建议对大数据量map进行遍历,建议按照limit,offset形式进行遍历,否者可能会由于数据过多,导致调用超时。

"use strict";

var SampleContract = function () {
   LocalContractStorage.defineMapProperty(this, "arrayMap");
   LocalContractStorage.defineMapProperty(this, "dataMap");
   LocalContractStorage.defineProperty(this, "size");
};

SampleContract.prototype = {
    init: function () {
        this.size = 0;
    },

    set: function (key, value) {
        var index = this.size;
        this.arrayMap.set(index, key);
        this.dataMap.set(key, value);
        this.size +=1;
    },

    get: function (key) {
        return this.dataMap.get(key);
    },

    len:function(){
      return this.size;
    },

    forEach: function(limit, offset){
        limit = parseInt(limit);
        offset = parseInt(offset);
        if(offset>this.size){
           throw new Error("offset is not valid");
        }
        var number = offset+limit;
        if(number > this.size){
          number = this.size;
        }
        var result  = "";
        for(var i=offset;i<number;i++){
            var key = this.arrayMap.get(i);
            var object = this.dataMap.get(key);
            result += "index:"+i+" key:"+ key + " value:" +object+"_";
        }
        return result;
    }
};

module.exports = SampleContract;

通过RPC API和星云链交互

05 通过RPC接口与星云链交互

星云链节点启动后可以通过RPC远程控制访问。星云链提供了一系列API来获取节点的信息,账号余额,发送交易和部署调用智能合约。

星云链的远程访问是GRPC实现的,通过代理(GRPC Gateway)也可以通过HTTP访问。HTTP访问是RESTful实现的接口,参数与GRPC的调用接口参数相同。

API

我们已经在每个星云节点中实现了RPC服务器和HTTP服务器,提供给用户丰富的接口来与星云节点交互。

接口模块

现在,星云节点的所有的接口被分为两个模块:API和Admin。

  • API:提供所有和用户私钥无关的接口
  • Admin:提供所有和用户私钥相关的接口

建议星云节点对外提供服务时,可以把API接口开放给公众,而将Admin接口开放给授权用户。

配置文件

星云节点中的RPC服务器和HTTP服务器都可以在节点的配置中配置对应的端口,以及开放的模块。

# 用户与节点交互的服务配置,同一台机器启动多个时注意修改端口防止占用
rpc {
    # gRPC API服务端口
    rpc_listen: ["127.0.0.1:8684"]
    # HTTP API服务端口
    http_listen: ["127.0.0.1:8685"]
    # 开放可对外提供http服务的模块
    http_module: ["api","admin"]
}
使用实例
HTTP

通过HTTP接口和星云节点交互。

GetNebState

我们可以调用API模块中的GetNebState接口来获取节点当前状态,包括所在链ID,最新区块,协议版本等等。

> curl -i -H Accept:application/json -X GET http://localhost:8685/v1/user/nebstate

{"result":{"chain_id":100,"tail":"0aa1cceb7801b110fdd5217ba0a4356780c940133924d1c1a4eb60336934dab1","lib":"0000000000000000000000000000000000000000000000000000000000000000","height":"479","protocol_version":"/neb/1.0.0","synchronized":false,"version":"0.7.0"}}

UnlockAccount

我们可以调用Admin模块中的UnlockAccount接口来在节点内存中解锁一个账户。所有解锁的账户都可以被用来直接发送交易,而不需要密码。

> curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/unlock -d '{"address":"n1NrMKTYESZRCwPFDLFKiKREzZKaN1nhQvz", "passphrase": "passphrase"}'

{"result":{"result":true}}
RPC

RPC服务器基于GRPC实现. GRPC的基于Protocol Buffers来做序列化,你可以在星云RPC Protobuf文件夹下找到所有的RPC相关的Proto文件定义。

这里有一些使用golang调用RPC接口的实例。

GetNebState

我们可以使用API模块中的GetNebState接口来获取节点当前状态。

import(
    "github.com/nebulasio/go-nebulas/rpc"
    "github.com/nebulasio/go-nebulas/rpc/pb"
)

// GRPC server connection address configuration
addr := fmt.Sprintf("127.0.0.1:%d",uint32(8684))
conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
    log.Warn("rpc.Dial() failed:", err)
}
defer conn.Close()

// API interface to access node status information
api := rpcpb.NewAPIServiceClient(conn)
resp, err := ac.GetNebState(context.Background(), & rpcpb.GetNebStateRequest {})
if err != nil {
    log.Println("GetNebState", "failed", err)
} else {
    log.Println("GetNebState tail", resp)
}

LockAccount

我们已经在之前使用HTTP接口把账户n1NrMKTYESZRCwPFDLFKiKREzZKaN1nhQvz解锁了。 我们可以调用Admin模块中的LockAccount再次锁定它。

import(
    "github.com/nebulasio/go-nebulas/rpc"
    "github.com/nebulasio/go-nebulas/rpc/pb"
)

// GRPC server connection address configuration
addr := fmt.Sprintf("127.0.0.1:%d",uint32(8684))
conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
    log.Warn("rpc.Dial() failed:", err)
}
defer conn.Close()

// Admin interface to access, lock account address
admin := rpcpb.NewAdminServiceClient(conn)
from := "n1NrMKTYESZRCwPFDLFKiKREzZKaN1nhQvz"
resp, err = management.LockAccount(context.Background(), & rpcpb.LockAccountRequest {Address: from})
if err != nil {
    log.Println("LockAccount", from, "failed", err)
} else {
    log.Println("LockAccount", from, "result", resp)
}
接口列表

更多的接口列表请参考官方文档。

完成

恭喜你成功走完了整个教程! 欢迎阅读下列指南来加入官方的测试网和主网,探索更加广阔的区块链世界。

加入测试网 & 加入主网

社区工具
官方星云文档

更多文档参见官网 资源下载页

Smart Contracts for Rookies

This tutorial is intended for beginners and will help you to understand the basics of smart contracts under Nebulas: you’ll be assisted to download the core and run a node instance, to set up a development environment, and to write some basic examples of smart contracts. In addition, you will find a quick reference guide for more seasoned developers, and a FAQ containing all the common doubts about the subject.

On [Github] by Arielsbecker

  1. What is a smart contract?
  2. Installing the core
  3. Setting up a development environment
  4. Hello World, a barebones smart contract
  5. AddressMetadata, a smart contract with storage capabilities
  6. PiggyBank, a smart contract that moves money around
  7. Reference guide
  8. FAQ
数据中心
数据中心对外提供了价格及链上数据查询接口,为浏览器及钱包等星云链生态应用提供底层数据支持。
节点访问

主网节点访问:

API URL Protocol
RESTful https://data.nebulas.io HTTP
接口详情
NRC20交易列表
按地址查询并获取NRC20交易列表【分页】。
Protocol Method API
HTTP GET tx/nrc20
Parameters

address 地址

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

txnList 交易列表

totalPage 总页码

maxDisplayCnt 最大显示数量

currentPage 当前页码

txnCnt 总数

Example
//Request
curl -X GET https://data.nebulas.io/tx/nrc20?address="n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG"&page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"txnList":[{"hash":"eb3c8d848679ad1786d5986da48bcc5d5732141fd6fccde0a9a3bfd78ae1d970","block":{"hash":"e188cb8f384f010cc5cfd84ed6779a96f74f7cb363383024b26502c0e85e767d","height":4841297},"from":{"hash":"n1QUMEs7qkvCDuimT2zYsbf62z9pRtofQiP"},"to":{"hash":"n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG"},"status":1,"value":"346000000000000000000","nonce":27,"timestamp":1595146350000,"currentTimestamp":1595149022681,"timeDiff":2672681,"type":"call","gasPrice":"20000000000","gasLimit":"830000","gasUsed":"63615","data":"{\"Function\":\"transfer\",\"Args\":\"[\\\"n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG\\\",\\\"346000000000000000000\\\"]\"}","contractAddress":"","executeError":"","tokenName":"MRH2","decimal":18,"txFee":"1272300000000000"},{"hash":"fc148958467c69d9ad1c0a5038a41b096d3c2302a604e0edecb52c7a6e9e1675","block":{"hash":"f7b9ed2a907c39a75789246ce071213d0903027ee014cece16063b397bde8e1e","height":4841292},"from":{"hash":"n1N9dDUn3AcdrjLhfCSzNxBGzfxHYTgKH1W"},"to":{"hash":"n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG"},"status":1,"value":"323000000000000000000","nonce":41,"timestamp":1595146275000,"currentTimestamp":1595149022681,"timeDiff":2747681,"type":"call","gasPrice":"20000000000","gasLimit":"830000","gasUsed":"63615","data":"{\"Function\":\"transfer\",\"Args\":\"[\\\"n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG\\\",\\\"323000000000000000000\\\"]\"}","contractAddress":"","executeError":"","tokenName":"MRH2","decimal":18,"txFee":"1272300000000000"}],"totalPage":243,"maxDisplayCnt":500,"currentPage":1,"txnCnt":485}}

交易详情
按地址查询交易详情。
Protocol Method API
HTTP GET tx/detail
Parameters

hash 交易哈希

Return

tx_hash 交易哈希

address_main 交易发起地址

address_supporting 交易接受地址

direction 交易方向

tx_type 交易类型

timestamp 时间戳

block_timestamp 交易时间戳

contract_address 合约地址

tx_value 交易金额

real_value 金额

gas_price 交易手续费价格

gas_limit 交易手续费上限

gas_used 交易使用量

statue 交易状态 0失败,1成功,2pending

block_height 交易区块高度

is_nrc20 是否为nrc20交易

Example
//Request
curl -X GET https://data.nebulas.io/tx/detail?hash="eb3c8d848679ad1786d5986da48bcc5d5732141fd6fccde0a9a3bfd78ae1d970"

//Result
{"code":0,"msg":"success","data":{"ext_info":{"data":"eyJGdW5jdGlvbiI6InRyYW5zZmVyIiwiQXJncyI6IltcIm4xekpqeVdWS3I4SGlMMWI2ZE43SkdCdENaRnVLUUhlbkxHXCIsXCIzNDYwMDAwMDAwMDAwMDAwMDAwMDBcIl0ifQ==","execute_error":"","execute_result":"\"\""},"tx_hash":"eb3c8d848679ad1786d5986da48bcc5d5732141fd6fccde0a9a3bfd78ae1d970","address_main":"n1QUMEs7qkvCDuimT2zYsbf62z9pRtofQiP","address_supporting":"n1zJjyWVKr8HiL1b6dN7JGBtCZFuKQHenLG","direction":"send","tx_type":"call","timestamp":1595146335,"block_timestamp":1595146350,"contract_address":"n1pxpisjJMsnVLrqHBnS52o8wsuMUsycfDV","tx_value":"0","real_value":"346000000000000000000","gas_price":"20000000000","gas_limit":830000,"gas_used":63615,"status":1,"block_height":4841297,"is_nrc20":true}}

交易列表
查询交易列表。
Protocol Method API
HTTP GET tx/list
Parameters

block_height 区块高度,可选参数

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 交易总数

total_page 总页数

current_page 当前页数

list 交易列表

server_timestamp 时间戳

Example
//Request
curl -X GET https://data.nebulas.io/tx/list?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/tx/list?page=2&page_size=2","previous":null,"count":13352569,"total_page":6676285,"current_page":1,"list":[{"tx_hash":"b3805aaf308fea5fc975e5b87632dbd27c7795c3b2bcc03b69263f8301bfcc6a","chainId":1,"address_from":"n1Tx1bxFsgyTcjQ82h59kMQ3XbZGXSiq8w2","address_to":"n1KxWR8ycXg7Kb9CPTtNjTTEpvka269PniB","value":"586003179640000000000","nonce":7181,"timestamp":1595152078,"block_timestamp":1595157150,"block_date":"20200719","tx_type":"binary","gas_price":"20000000000","gas_limit":200000,"gas_used":20000,"contract_address":"","status":1,"execute_error":"","execute_result":"","block_height":4842017},{"tx_hash":"a920a35b4450d1cb7395b96a6b4f3ae3a616a6f0a129f6586efb6f6b2f71c13f","chainId":1,"address_from":"n1EoNsJNXG1tN3z9rvjwPKoBXbJMqAjmESC","address_to":"n1gtEoDBNnPrAHzRY9tSnpSSg3QppLYXUdR","value":"0","nonce":4915,"timestamp":2147483647,"block_timestamp":1595157075,"block_date":"20200719","tx_type":"call","gas_price":"20000000000","gas_limit":9000000,"gas_used":989604,"contract_address":"","status":1,"execute_error":"","execute_result":"{\"hasNext\":false,\"period\":298,\"start\":4836000,\"end\":4842000,\"page\":4}","block_height":4842012}],"server_timestamp":1595157487445}}

未上链交易列表
查询未上链的交易列表。
Protocol Method API
HTTP GET tx/list/pending
Parameters

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 交易总数

total_page 总页数

current_page 当前页数

list 交易列表

server_timestamp 时间戳

Example
//Request
curl -X GET https://data.nebulas.io/tx/list/pending?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/tx/list/pending?page=2&page_size=2","previous":null,"count":156,"total_page":78,"current_page":1,"list":[{"tx_hash":"4138b43575e419a534a878be45dd8e43f4ddb61ac122ed82a8d5050fde156f7b","chainId":1,"address_from":"n1Gfg8uqtFsvGKZn6XtVmEb116ZtagRHDoe","address_to":"n22CMMXaxkAjjbsWtVXSmFJgEsnVZ3UwUWf","value":"0","nonce":2818,"timestamp":1595154990,"block_timestamp":null,"block_date":null,"tx_type":"pod","gas_price":"1000000000000","gas_limit":50000000000,"gas_used":null,"contract_address":null,"status":2,"execute_error":"","execute_result":"","block_height":null},{"tx_hash":"9a6a2fa7c02c18a0f61c96cf4b545da6e41bf5e64a8641062e66c81134b924f8","chainId":1,"address_from":"n1bc9szRj57bD4HqM5672v92gnBvPayacJE","address_to":"n22CMMXaxkAjjbsWtVXSmFJgEsnVZ3UwUWf","value":"0","nonce":2894,"timestamp":1595154990,"block_timestamp":null,"block_date":null,"tx_type":"pod","gas_price":"1000000000000","gas_limit":50000000000,"gas_used":null,"contract_address":null,"status":2,"execute_error":"","execute_result":"","block_height":null}],"server_timestamp":1595157812578}}

按地址查询交易列表
按地址查询交易列表。
Protocol Method API
HTTP GET tx/listByAddress
Parameters

address 地址

is_nrc20 是否为nrc20交易

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 交易总数

total_page 总页数

current_page 当前页数

list 交易列表

server_timestamp 时间戳

Example
//Request
curl -X GET https://data.nebulas.io/tx/listByAddress?address=n1Gfg8uqtFsvGKZn6XtVmEb116ZtagRHDoe&page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/tx/listByAddress?address=n1Gfg8uqtFsvGKZn6XtVmEb116ZtagRHDoe&page=2&page_size=2","previous":null,"count":2829,"total_page":1415,"current_page":1,"list":[{"tx_hash":"9e862c72614aba4138e8e1fcbf44ef0a8f2007520015e899bf75da3d9136049b","chainId":1,"address_from":"n1Gfg8uqtFsvGKZn6XtVmEb116ZtagRHDoe","address_to":"n22CMMXaxkAjjbsWtVXSmFJgEsnVZ3UwUWf","value":"0","nonce":2818,"timestamp":1595154930,"block_timestamp":1595154990,"block_date":"20200719","tx_type":"pod","gas_price":"1000000000000","gas_limit":50000000000,"gas_used":150885,"contract_address":"","status":1,"execute_error":"","execute_result":"\"\"","block_height":4841873},{"tx_hash":"4138b43575e419a534a878be45dd8e43f4ddb61ac122ed82a8d5050fde156f7b","chainId":1,"address_from":"n1Gfg8uqtFsvGKZn6XtVmEb116ZtagRHDoe","address_to":"n22CMMXaxkAjjbsWtVXSmFJgEsnVZ3UwUWf","value":"0","nonce":2818,"timestamp":1595154990,"block_timestamp":null,"block_date":null,"tx_type":"pod","gas_price":"1000000000000","gas_limit":50000000000,"gas_used":null,"contract_address":null,"status":2,"execute_error":"","execute_result":"","block_height":null}],"server_timestamp":1595158063474}}

历史交易统计
历史交易统计。
Protocol Method API
HTTP GET tx/count/history
Parameters

days 统计天数,可选参数,默认为15

Return

price nas价格

date 日期

transaction_count 交易数量

Example
//Request
curl -X GET https://data.nebulas.io/tx/count/history?days=2

//Result
{"code":0,"msg":"success","data":[{"price":0.4775322101523956,"date":"20200718","transaction_count":2354},{"price":0,"date":"20200717","transaction_count":2231}]}

当日交易统计
当日交易统计。
Protocol Method API
HTTP GET tx/count/today
Parameters

None

Return

count 交易数

Example
//Request
curl -X GET https://data.nebulas.io/tx/count/today

//Result
{"code":0,"msg":"success","data":{"count":1074}}

区块列表
区块列表。
Protocol Method API
HTTP GET block/list
Parameters

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 交易总数

total_page 总页数

current_page 当前页数

list 区块列表

Example
//Request
curl -X GET https://data.nebulas.io/block/list?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/block/list?page=2&page_size=2","previous":null,"count":4842132,"total_page":2421066,"current_page":1,"list":[{"block_hash":"c7e7862a725acb12c599cb1bbf5dd2e9781a4edffb4d850d2ad70776f0517dcc","parent_hash":"87f27c9e71d6797eb80fcb4f4ee39d87c7ea153f7124b9679851fb672e6631eb","height":4842132,"tx_count":1,"nonce":0,"coinbase":"n1HEitaphPaJG1iJHVU5AVtC7GvhHmKf1YV","timestamp":1595158875,"chain_id":1,"state_root":"5f173a48643475b17564114b761112e2c091f12916606a91f3a9d3dff9037532","txs_root":"70204ae37a62a318de69fe33505edb10a3983bc417394c97186c56d831c9a3ff","events_root":"442586640329dc93208dae2177d924bf0dc58663b12ec85caa7ef87c87012beb","miner":"n1RKTn2SXD67TvBaf55eHcFHa1AKzJcmrh2","randomSeed":"ac841e144b2cc1725bcd26f0dca14697ceca2e41c2021fd4a481a7c05fdb6890","randomProof":"e4e895173487a7e69fe917a7e987dc646736f5503f0f4432505ddf1755f3e3478c9f8a395cda5b273c5db8b5554c2b0c0079283d4b51c12ce925b4ae1d0a3cc1045a45d40d9e575f4f8acb20f276fec1023cc0989feabab87d76a7d52814dbced2bb5712630c0c43e4cacdba54bd3f6bd0414a15346b82ba59636c49f0cfece864","is_finality":false},{"block_hash":"87f27c9e71d6797eb80fcb4f4ee39d87c7ea153f7124b9679851fb672e6631eb","parent_hash":"d7e6b5705addf45c5e33f2953ebb894be22fe6b6f4412abfe5e548aa22c0ad8b","height":4842131,"tx_count":1,"nonce":0,"coinbase":"n1bSo5dMmVejyTnD9fBKsWmufrsTnoMUJVJ","timestamp":1595158860,"chain_id":1,"state_root":"bd9d19401e797fdfcff2cca354d7b7f1db0b4aad08407242dab9facdb1334aef","txs_root":"e120e7f50f08a2a1773e1782668c3d9f4312c5fffb1317f22fd6ec37b9a47161","events_root":"494985b7def3ac8c18f96ecf0464e966fe5a11f948be435a48870a5e0eb1d867","miner":"n1QyuEwxQED86Wq1ADMCTFjCay4BUnJDBGX","randomSeed":"a8866802b63d386edfc1ee009fa2d8e73163192382e27857cd087aa2afa59c46","randomProof":"2a2f3d701f0aa89695a7a69f9d48c7fc136cb8cf6cde2ab51187f8da8f72f90457ed8cc8530b040cdb4d79736f46dfdb4ce1a3a5e8fcbfe1df74c42e0b8d23b30474230844ec470f97014f8d0064fb5b077cab04afaeba5cb5fed42b3401fbe77abd395ac8489f128cf46bd96ffdc58e9f760effc6baadba0239bb7f0bebc9458c","is_finality":false}]}}

区块详情
查询区块详情。
Protocol Method API
HTTP GET block/detail
Parameters

h 区块哈希或高度

Return

gas_info gas信息

dynasty 出块地址列表

node 出块节点信息

block_hash 区块哈希

parent_hash 父亲区块哈希

height 区块高度

tx_count 交易数量

nonce 序列

coinbase 奖励地址

timestamp 时间戳

chain_id 链ID

miner 出块地址

is_finality 是否为不可逆区块

Example
//Request
curl -X GET https://data.nebulas.io/block/detail?h=4820298

//Result
{"code":0,"msg":"success","data":{"gas_info":{"gas_limit":"0","gas_reward":"0","avg_gas_price":"0"},"dynasty":["n1FfapZbhjFb2Lt9vxuSW7odjEPh8DeYsFG","n1FoHjHE3rMGsYEEPgadvrHeuiVmvkL44p3","n1HpAFoBMnhMRSJ4jXECWbhAVbx1fzh1Pcj","n1Jkdiq1H1HSXYJXtvDDkYm84Tmapo4hhMv","n1NSK8TiFQRGXzxcc5jmTCuCLbnCejUcw8s","n1Qm4ZaRf7HnQNVhg13BxjprVW42vAi8dYY","n1QyuEwxQED86Wq1ADMCTFjCay4BUnJDBGX","n1S7CvsNWbPtaxH8pMqSby8hW6EkHExM59C","n1Sg1yFp4fGFLtvmxHXBrcATSBWwNJfUyBc","n1SqTLCGr56ZpT58Gj3wLfKVmQ8GXvr9ZYG","n1TURfNzQzT3aG4WDDebhrC1VJ6VbXvh2ZC","n1VwmLzc8VGbLa6o49WVA4fctJszVo3JPgQ","n1YRxpRaKikNPJRUScnWaqM9zKEPbsbGsgD","n1ZciM6PYAkEjsXUfHCwYwW9gNWqU9AnXax","n1bYLRcBtA5McKxbdqAXWMiDygjGnFpMrH2","n1baJw6b6LhoTz1ARaLBhoESuV5wEMuLmCf","n1bc9szRj57bD4HqM5672v92gnBvPayacJE","n1cbzD8Y1Pgw94eojoKd7nGQdA1JtML4daT","n1d2RCScDDxVA7PgJNT1N1kuue7raT9KMtG","n1dHKZTnMA2hmocLD35UfqG6kwDQ3Uq92nS","n1dXgssVuGTGPCnfG3eh2huEpmP542GybSd"],"node":{"period":23001,"info":{"avatar":"vko6lukmtl.png","email":"support@keysinteractive.com","id":"keys01","income-distribute":"We will distribute 80% of the block rewards back to our voters.\n\nPayout frequency is weekly (every 168 polling cycle).","name":"Keys | Share 80%","server":"Memory: 32GB of RAM\nStorage: 1TB SSD\nCPU: 64-bit\nProcessor: 8 cores (3.2 GHz each)\nNetwork: 1 Gbps","summary":"We provide staking/validation services for Proof of Stake networks for more than a year. \n\nOur primary goal is to ensure stable and secure block production by maintaining high-performance hardware and the optimal infrastructure.","telegram":null,"twitter":"keys_tech","website":null,"wechat":null}},"block_hash":"88131b5416e2b564fe478317bf842110f10f27ab654f03a33a6be2b222149afc","parent_hash":"b33862a67bfc69ec2ee790748d32104b7ac863a2e891659e3e8bf0e90b3700b3","height":4820298,"tx_count":0,"nonce":0,"coinbase":"n1VNg5f4E3KW9sjif2BeQ8dshdK9wgKwwQv","timestamp":1594831260,"chain_id":1,"miner":"n1Sg1yFp4fGFLtvmxHXBrcATSBWwNJfUyBc","is_finality":true,"server_timestamp":1595163814094}}

最高区块
查询最高区块。
Protocol Method API
HTTP GET block/max
Parameters

None

Return

height 高度

server_timestamp 时间戳

Example
//Request
curl -X GET https://data.nebulas.io/block/max

//Result
{"code":0,"msg":"success","data":{"height":4842503.0,"server_timestamp":1595164447597}}

合约列表
查询合约列表。
Protocol Method API
HTTP GET contracts
Parameters

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 合约列表

Example
//Request
curl -X GET https://data.nebulas.io/contracts?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/contracts?page=2&page_size=2","previous":null,"count":15423,"total_page":7712,"current_page":1,"list":[{"block_timestamp":1593176850000,"type":"NORMAL","address":"n1orbrZ5r7wa6C4dBmCo46ZkPtMiMaS5GcP","creator_address":"n1SD9Bp3pEDy1g8acG7qmVJu5DPWreLeqsm","deploy_tx_hash":"bff2e9c73c1c3a85ac5911614fef1892f9f35cbd253aa6eef79e905ebca2b150","block_height":4710023},{"block_timestamp":1591266855000,"type":"NORMAL","address":"n1nvqH9aE57USRPfGm33sTRPac9kRjsXiKu","creator_address":"n1QaTZhe1TABmigiGsutCJR2kdrhHNXqNeZ","deploy_tx_hash":"ce06c9e6a5b5bd0ca985df3b837f39a6bd475e45247b392629badeb9b64d3450","block_height":4582759}]}}

token列表
查询token列表。
Protocol Method API
HTTP GET token/list
Parameters

None

Return list

token_name token名称

description token描述

contract 合约地址

total 总数

token_decimals token位数

Example
//Request
curl -X GET https://data.nebulas.io/token/list

//Result
{"code":0,"msg":"success","data":[{"token_name":"ATP","description":"ATP-DESC","contract":"n1zUNqeBPvsyrw5zxp9mKcDdLTjuaEL7s39","total":"10000000000000000000000000000","token_decimals":18},{"token_name":"WITI","description":"WITI-DESC","contract":"n1hiWG7Ce8HhTaJGzSJoAaJ9w1CJd7Do2rm","total":"200000000000000000","token_decimals":8},{"token_name":"NAT","description":"NAT","contract":"n1mpgNi6KKdSzr7i5Ma7JsG5yPY9knf9He7","total":"100000000000000000000000000000","token_decimals":18},{"token_name":"WICM","description":"WITTICISM TOKEN","contract":"n1jJHWrXMysNm7odsUed3RGddiEH1jbJXtt","total":"200000000000000000000000000","token_decimals":18},{"token_name":"NAX","description":"NAX","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","total":"10000000000000000000","token_decimals":9},{"token_name":"MRH2","description":"MRH2","contract":"n1pxpisjJMsnVLrqHBnS52o8wsuMUsycfDV","total":"1000000000000000000000000000","token_decimals":18}]}

token详情
查询token详情。
Protocol Method API
HTTP GET token/detail
Parameters

token token名称

contract 合约地址,和token二选一请求

Return

token_name token名称

description token描述

contract 合约地址

total 总数

token_decimals token位数

Example
//Request
curl -X GET https://data.nebulas.io/token/detail?token=NAX

//Result
{"code":0,"msg":"success","data":{"token_name":"NAX","description":"NAX","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","total":"10000000000000000000","token_decimals":9}}

token交易列表
查询某个token的交易详情。
Protocol Method API
HTTP GET token/tx/list
Parameters

token token名称

contract 合约地址,和token二选一请求

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 交易列表

token token信息

Example
//Request
curl -X GET https://data.nebulas.io/token/tx/list?token=NAX&page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/token/tx/list?page=2&page_size=2&token=NAX","previous":null,"count":8318,"total_page":4159,"current_page":1,"list":[{"token":{"token_name":"NAX","description":"NAX","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","total":"10000000000000000000","token_decimals":9},"tx_hash":"63d2395551bc1ad95138d2efeee941573a6eb7154c8db532988f64663e1ea2b3","address_main":"n1dTbtBu98qsxLNmmZKRWmh5qMS6L2pSzKy","address_supporting":"n1aM6Bag362YnAVtjLkRWzFVjQdrZpsNmwk","direction":"send","tx_type":"call","timestamp":1595164824,"block_timestamp":1595164845,"contract_address":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","tx_value":"0","real_value":"2486000000000","gas_price":"20000000000","gas_limit":810000,"gas_used":21105,"status":1,"block_height":4842530,"is_nrc20":true},{"token":{"token_name":"NAX","description":"NAX","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","total":"10000000000000000000","token_decimals":9},"tx_hash":"7063f2234170f43747149711bf119a03cdd8b51405212114c7c4ae9021295100","address_main":"n1dTbtBu98qsxLNmmZKRWmh5qMS6L2pSzKy","address_supporting":"n1aM6Bag362YnAVtjLkRWzFVjQdrZpsNmwk","direction":"send","tx_type":"call","timestamp":1595164477,"block_timestamp":1595164500,"contract_address":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","tx_value":"0","real_value":"3707000000000","gas_price":"20000000000","gas_limit":810000,"gas_used":20673,"status":0,"block_height":4842507,"is_nrc20":true}],"server_timestamp":1595165282662,"token":{"token_name":"NAX","description":"NAX","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","total":"10000000000000000000","token_decimals":9}}}

token持有列表
查询token持有列表。
Protocol Method API
HTTP GET token/holders
Parameters

token token名称

contract 合约地址,和token二选一请求

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 地址列表

Example
//Request
curl -X GET https://data.nebulas.io/token/holders?token=NAX&page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/token/holders?page=2&page_size=2&token=NAX","previous":null,"count":1399,"total_page":700,"current_page":1,"list":[{"address":"n1QbEBKhsGxJUahRQXqV88T9oi4d9ywK453","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","balance":"390084128964550818"},{"address":"n214bLrE3nREcpRewHXF7qRDWCcaxRSiUdw","contract":"n1etmdwczuAUCnMMvpGasfi8kwUbb2ddvRJ","balance":"100395915000000000"}]}}

NAS持有列表
查询NAS持有列表。
Protocol Method API
HTTP GET holders
Parameters

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 地址列表

Example
//Request
curl -X GET https://data.nebulas.io/holders?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/holders?page=2&page_size=2","previous":null,"count":339598,"total_page":400,"current_page":1,"list":[{"tx_count":12,"percentage":59.493851,"address":"n1gczhpkT54RaT4PB55CNoYbqmEQcfo4hqq","nonce":0,"type":88,"balance":"35901357730000000000000000","date_of_found":"20191009","rank":1.0},{"tx_count":88344,"percentage":20.707855,"address":"n1KxWR8ycXg7Kb9CPTtNjTTEpvka269PniB","nonce":49290,"type":87,"balance":"12496083258556280663196250","date_of_found":"20180424","rank":2.0}]}}

NAX分发历史
查询NAX分发历史。
Protocol Method API
HTTP GET nax/history
Parameters

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 历史列表

Example
//Request
curl -X GET https://data.nebulas.io/nax/history?page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/nax/history?page=2&page_size=2","previous":null,"count":299,"total_page":150,"current_page":1,"list":[{"nas_price":0.4775322101523956,"nax_price":0.002492307692307692,"start_timestamp":"2020-07-18T10:08:00","end_timestamp":"2020-07-19T11:08:15","total_distributed_nax":"1233673083506519749","total_destroyed_nax":"1351842110126118095","total_vote_nax":"514052192214285714.2857","avg_reward_rate":0.950916,"created_at":"2020-07-18T10:12:16","stage":298,"start":4836000,"end":4842000,"estimate_nax":"7421906713080442","distributed_nax":"3425198001737814","destroyed_nax":"3996708711342628","total_supplied_nax":"1233673083506411630","pledged_nas":"27848503662499186630500000","total_supplied_nas":"60343663688079320000000000","status":1},{"nas_price":0.4541347259763038,"nax_price":0.002418367088607595,"start_timestamp":"2020-07-17T09:07:00","end_timestamp":"2020-07-18T10:08:00","total_distributed_nax":"1230247885504781935","total_destroyed_nax":"1347845401414775467","total_vote_nax":"513406552357142857.1429","avg_reward_rate":0.932828,"created_at":"2020-07-17T09:12:05","stage":297,"start":4830000,"end":4836000,"estimate_nax":"7429336049129572","distributed_nax":"3429332827592696","destroyed_nax":"4000003221536876","total_supplied_nax":"1230247885504674237","pledged_nas":"27850287662499186630500000","total_supplied_nas":"58103599263060000000000000","status":1}]}}

NAX统计
查询NAX统计。
Protocol Method API
HTTP GET nax/summary
Parameters

None

Return

last_distributed_nax 上次nax分发金额

total_distributed_nax nax总分发金额

current_pledged_nas 当前质押nas

current_total_nas 当前流通nas

list 历史分发列表

Example
//Request
curl -X GET https://data.nebulas.io/nax/summary

//Result
{"code":0,"msg":"success","data":{"last_distributed_nax":"3425198001737814","total_distributed_nax":"1233673083506519749","current_pledged_nas":"27856286662499186630500000","current_total_nas":"71910268469860000000000000","estimate_nax":"7414484806367362","end_height":4848000,"list":[{"nas_price":0.4775322101523956,"nax_price":0.002492307692307692,"start_timestamp":"2020-07-18T10:08:00","end_timestamp":"2020-07-19T11:08:15","total_distributed_nax":"1233673083506519749","total_destroyed_nax":"1351842110126118095","total_vote_nax":"514052192214285714.2857","avg_reward_rate":0.950916,"created_at":"2020-07-18T10:12:16","stage":298,"start":4836000,"end":4842000,"estimate_nax":"7421906713080442","distributed_nax":"3425198001737814","destroyed_nax":"3996708711342628","total_supplied_nax":"1233673083506411630","pledged_nas":"27848503662499186630500000","total_supplied_nas":"60343663688079320000000000","status":1},{"nas_price":0.4541347259763038,"nax_price":0.002418367088607595,"start_timestamp":"2020-07-17T09:07:00","end_timestamp":"2020-07-18T10:08:00","total_distributed_nax":"1230247885504781935","total_destroyed_nax":"1347845401414775467","total_vote_nax":"513406552357142857.1429","avg_reward_rate":0.932828,"created_at":"2020-07-17T09:12:05","stage":297,"start":4830000,"end":4836000,"estimate_nax":"7429336049129572","distributed_nax":"3429332827592696","destroyed_nax":"4000003221536876","total_supplied_nax":"1230247885504674237","pledged_nas":"27850287662499186630500000","total_supplied_nas":"58103599263060000000000000","status":1},{"nas_price":0.46206315262654346,"nax_price":0.0023882391304347827,"start_timestamp":"2020-07-16T08:06:30","end_timestamp":"2020-07-17T09:07:00","total_distributed_nax":"1226818552677189239","total_destroyed_nax":"1343845398193238591","total_vote_nax":"511429083862068965.5172","avg_reward_rate":0.999776,"created_at":"2020-07-16T08:09:31","stage":296,"start":4824000,"end":4830000,"estimate_nax":"7436772821951523","distributed_nax":"3435182528484704","destroyed_nax":"4001590293466819","total_supplied_nax":"1226818552677081958","pledged_nas":"27865941662499186630500000","total_supplied_nas":"60326540408079320000000000","status":1},{"nas_price":0.49049006922562666,"nax_price":0.0026480666666666665,"start_timestamp":"2020-07-15T07:06:30","end_timestamp":"2020-07-16T08:06:30","total_distributed_nax":"1223383370148704535","total_destroyed_nax":"1339843807899771772","total_vote_nax":"507907569000000000.0000","avg_reward_rate":0.930706,"created_at":"2020-07-15T07:10:30","stage":295,"start":4818000,"end":4824000,"estimate_nax":"7444217038990514","distributed_nax":"3475204652507346","destroyed_nax":"3969012386483168","total_supplied_nax":"1223383370148597666","pledged_nas":"28158410662499186630500000","total_supplied_nas":"58080768183060000000000000","status":1},{"nas_price":0.4789442682472688,"nax_price":0.002679741935483871,"start_timestamp":"2020-07-14T06:06:30","end_timestamp":"2020-07-15T07:06:30","total_distributed_nax":"1219908165496197189","total_destroyed_nax":"1335874795513288604","total_vote_nax":"505217907310344827.5862","avg_reward_rate":0.935081,"created_at":"2020-07-14T06:10:31","stage":294,"start":4812000,"end":4818000,"estimate_nax":"7451668707698212","distributed_nax":"3470160088219490","destroyed_nax":"3981508619478722","total_supplied_nax":"1219908165496090731","pledged_nas":"28085431662499186630500000","total_supplied_nas":"60309417128079320000000000","status":1},{"nas_price":0.48084590963152873,"nax_price":0.002551816091954023,"start_timestamp":"2020-07-13T05:06:30","end_timestamp":"2020-07-14T06:06:30","total_distributed_nax":"1216438005407977699","total_destroyed_nax":"1331893286893809882","total_vote_nax":"498465746892857142.8571","avg_reward_rate":0.964755,"created_at":"2020-07-13T05:11:31","stage":293,"start":4806000,"end":4812000,"estimate_nax":"7459127835533746","distributed_nax":"3485080668820116","destroyed_nax":"3974047166713630","total_supplied_nax":"1216438005407871672","pledged_nas":"28173983662499186630500000","total_supplied_nas":"60300855488079320000000000","status":1},{"nas_price":0.48637544312914666,"nax_price":0.00253512,"start_timestamp":"2020-07-12T04:04:15","end_timestamp":"2020-07-13T05:06:30","total_distributed_nax":"1212952924739157583","total_destroyed_nax":"1327919239727096252","total_vote_nax":"493814339137931034.4828","avg_reward_rate":1.025423,"created_at":"2020-07-12T04:08:16","stage":292,"start":4800000,"end":4806000,"estimate_nax":"7466594429963709","distributed_nax":"3477434441786175","destroyed_nax":"3989159988177534","total_supplied_nax":"1212952924739051971","pledged_nas":"28080070662499186630500000","total_supplied_nas":"60292293848079320000000000","status":1},{"nas_price":0.4700681555242065,"nax_price":0.002359413043478261,"start_timestamp":"2020-07-11T03:04:15","end_timestamp":"2020-07-12T04:04:15","total_distributed_nax":"1209475490297371408","total_destroyed_nax":"1323930079738918718","total_vote_nax":"490152026857142857.1429","avg_reward_rate":1.037342,"created_at":"2020-07-11T03:09:15","stage":291,"start":4794000,"end":4800000,"estimate_nax":"7474068498462171","distributed_nax":"3487928053271783","destroyed_nax":"3986140445190388","total_supplied_nax":"1209475490297266225","pledged_nas":"28132645662499186630500000","total_supplied_nas":"60283732208079320000000000","status":1},{"nas_price":0.4345778883513563,"nax_price":0.0022704367816091956,"start_timestamp":"2020-07-10T02:04:15","end_timestamp":"2020-07-11T03:04:15","total_distributed_nax":"1205987562244099625","total_destroyed_nax":"1319943939293728330","total_vote_nax":"487198695321428571.4286","avg_reward_rate":1.011173,"created_at":"2020-07-10T02:09:16","stage":290,"start":4788000,"end":4794000,"estimate_nax":"7481550048510682","distributed_nax":"3498863698327820","destroyed_nax":"3982686350182862","total_supplied_nax":"1205987562243994834","pledged_nas":"28188624662499186630500000","total_supplied_nas":"60275170568079320000000000","status":1},{"nas_price":0.42344173104069566,"nax_price":0.0022432717391304346,"start_timestamp":"2020-07-09T01:04:15","end_timestamp":"2020-07-10T02:04:15","total_distributed_nax":"1202488698545771805","total_destroyed_nax":"1315961252943545468","total_vote_nax":"486888939275862068.9655","avg_reward_rate":1.04999,"created_at":"2020-07-09T01:08:16","stage":289,"start":4782000,"end":4788000,"estimate_nax":"7489039087598280","distributed_nax":"3501787111156831","destroyed_nax":"3987251976441449","total_supplied_nax":"1202488698545667432","pledged_nas":"28179961662499186630500000","total_supplied_nas":"60266608928079320000000000","status":1},{"nas_price":0.4371542430544301,"nax_price":0.0022258924731182795,"start_timestamp":"2020-07-08T00:04:00","end_timestamp":"2020-07-09T01:04:15","total_distributed_nax":"1198986911434614974","total_destroyed_nax":"1311974000967104019","total_vote_nax":"484083464107142857.1429","avg_reward_rate":1.054589,"created_at":"2020-07-08T00:09:16","stage":288,"start":4776000,"end":4782000,"estimate_nax":"7496535623221502","distributed_nax":"3504034562399862","destroyed_nax":"3992501060821640","total_supplied_nax":"1198986911434511012","pledged_nas":"28165847662499186630500000","total_supplied_nas":"58000859403060000000000000","status":1},{"nas_price":0.4082020724468925,"nax_price":0.00200694623655914,"start_timestamp":"2020-07-06T23:04:00","end_timestamp":"2020-07-08T00:04:00","total_distributed_nax":"1195482876872215112","total_destroyed_nax":"1307981499906282379","total_vote_nax":"480995342107142857.1429","avg_reward_rate":1.09882,"created_at":"2020-07-06T23:08:05","stage":287,"start":4770000,"end":4776000,"estimate_nax":"7504039662884386","distributed_nax":"3204930295742230","destroyed_nax":"4299109367142156","total_supplied_nax":"1195482876872111553","pledged_nas":"25732193662499186630500000","total_supplied_nas":"60249485648079320000000000","status":1},{"nas_price":0.3761060705104889,"nax_price":0.0019144444444444445,"start_timestamp":"2020-07-05T22:03:15","end_timestamp":"2020-07-06T23:04:00","total_distributed_nax":"1192277946576472882","total_destroyed_nax":"1303682390539140223","total_vote_nax":"480877008448275862.0690","avg_reward_rate":1.102562,"created_at":"2020-07-05T22:08:16","stage":286,"start":4764000,"end":4770000,"estimate_nax":"7511551214098485","distributed_nax":"3327871474520931","destroyed_nax":"4183679739577554","total_supplied_nax":"1192277946576369732","pledged_nas":"26688768656599066630500000","total_supplied_nas":"60240924008079320000000000","status":1},{"nas_price":0.3670408923895109,"nax_price":0.0018900108695652175,"start_timestamp":"2020-07-04T21:02:45","end_timestamp":"2020-07-05T22:03:15","total_distributed_nax":"1188950075101951951","total_destroyed_nax":"1299498710799562669","total_vote_nax":"480231897821428571.4286","avg_reward_rate":1.049588,"created_at":"2020-07-04T21:06:46","stage":285,"start":4758000,"end":4764000,"estimate_nax":"7519070284382868","distributed_nax":"3318646097157277","destroyed_nax":"4200424187225591","total_supplied_nax":"1188950075101849212","pledged_nas":"26584389656599066630500000","total_supplied_nas":"60232362368079320000000000","status":1},{"nas_price":0.3664989544953158,"nax_price":0.001934,"start_timestamp":"2020-07-03T20:02:45","end_timestamp":"2020-07-04T21:02:45","total_distributed_nax":"1185631429004794674","total_destroyed_nax":"1295298286612337078","total_vote_nax":"478950430892857142.8571","avg_reward_rate":1.031921,"created_at":"2020-07-03T20:06:48","stage":284,"start":4752000,"end":4758000,"estimate_nax":"7526596881264132","distributed_nax":"3353989426569032","destroyed_nax":"4172607454695100","total_supplied_nax":"1185631429004692345","pledged_nas":"26836828656599066630500000","total_supplied_nas":"57955197243060000000000000","status":1}]}}

NAX地址分发历史
查询NAX地址分发历史。
Protocol Method API
HTTP GET nax/profits
Parameters

address 地址

page 页码;可选参数,默认1

page_size 分页大小;可选参数,默认20

Return

next 下一页请求URL

count 总数

total_page 总页数

current_page 当前页数

list 数据列表

Example
//Request
curl -X GET https://data.nebulas.io/nax/profits?address=n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg&page=1&page_size=2

//Result
{"code":0,"msg":"success","data":{"next":"http://data.nebulas.io/nax/profits?address=n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg&page=2&page_size=2","previous":null,"count":10,"total_page":5,"current_page":1,"list":[{"address":"n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg","tx_hash":"ed36d5bc52f48c55a92551044b14fd1e35b9e9b3a57226f9e910b8e9709ea9c0","block_timestamp":1593710040,"profit":"-1000000000","stage":-1,"source":1,"block_height":4745566},{"address":"n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg","tx_hash":"c27185fc66fd346fb53dde96cfa73fec957fc40e2e38c19cf5f051fd42bcc516","block_timestamp":1593710115,"profit":"-1000000000","stage":-1,"source":1,"block_height":4745571}]}}

地址信息
查询地址信息。
Protocol Method API
HTTP GET address/info
Parameters

address 地址

Return

address 地址

nonce 交易数

type 类型,87普通地址,88合约地址

balance 余额

date_of_found 创建日期

Example
//Request
curl -X GET https://data.nebulas.io/address/info?address=n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg

//Result
{"code":0,"msg":"success","data":{"contract":{},"address":"n1d4wXxTVmK4rzyiN1vxhMuzxT441bxr8Fg","nonce":124,"type":87,"balance":"22386060161435630252100","date_of_found":"20200130"}}

地址统计
查询地址统计。
Protocol Method API
HTTP GET address/count/history
Parameters

days 日期,可选参数,默认30

Return

created_at 创建时间

updated_at 更新时间

date 日期

all_address_count 地址数

contract_count 合约数

Example
//Request
curl -X GET https://data.nebulas.io/address/count/history?days=5

//Result
{"code":0,"msg":"success","data":[{"created_at":"2020-07-19T00:00:00","updated_at":"2020-07-19T00:00:00","date":"20200718","all_address_count":339596,"contract_count":15922},{"created_at":"2020-07-18T00:00:00","updated_at":"2020-07-18T00:00:00","date":"20200717","all_address_count":339587,"contract_count":15922},{"created_at":"2020-07-17T00:00:00","updated_at":"2020-07-17T00:00:00","date":"20200716","all_address_count":339581,"contract_count":15922},{"created_at":"2020-07-17T00:00:00","updated_at":"2020-07-17T00:00:00","date":"20200716","all_address_count":339581,"contract_count":15922},{"created_at":"2020-07-16T00:00:00","updated_at":"2020-07-16T00:00:00","date":"20200715","all_address_count":339577,"contract_count":15922}]}

公链统计信息
查询公链统计信息。
Protocol Method API
HTTP GET chain/summary
Parameters

None

Return

block_count 区块数

tx_count 交易数

contract_count 合约数量

normal_address_count 地址数量

Example
//Request
curl -X GET https://data.nebulas.io/chain/summary

//Result
{"code":0,"msg":"success","data":{"block_count":4842814,"tx_count":13075893,"contract_count":15922,"normal_address_count":323676}}

DApp开发
智能合约

你可以从一些智能合约案例开始入手:

合约实例

This tutorial is intended for beginners and will help you to understand the basics of smart contracts under Nebulas: you‘ll be assisted to download the core and run a node instance, to set up a development environment, and to write some basic examples of smart contracts. In addition, you will find a quick reference guide for more seasoned developers, and a FAQ containing all the common doubts about the subject.

On [Github] by Arielsbecker

  1. What is a smart contract?
  2. Installing the core
  3. Setting up a development environment
  4. Hello World, a barebones smart contract
  5. AddressMetadata, a smart contract with storage capabilities
  6. PiggyBank, a smart contract that moves money around
  7. Reference guide
  8. FAQ
智能合约语言

Nebulas支持两种智能合约语言: * JavaScript * TypeScript

Chrome V8是一个由Chromium开发的广受欢迎的JavaScript执行引擎,用于Google Chrome及Chromium中,Nebulas选用Chrome V8作为这两种智能合约语言的执行引擎。

执行模型

智能合约的执行模型如下图:

Smart Contract Execution Model

Smart Contract Execution Model

合约执行步骤: * 打包合约代码和相关参数到本次Transaction中,部署合约到星云链。 * 合约的执行分为两个阶段: * 预处理阶段:注入跟踪指令。 * 执行阶段:生成可执行代码并运行。

合约

Nebulas中的合约与面向对象语言中的类相似,包含状态变量和可以修改变量的函数。

编写合约

合约必须是JavaScript或TypeScript中的原型对象或类。

合约必须包含一个init函数,只会在部署合约时执行。以_开头的函数是私有的,用户无法直接调用。其他都是公有的,用户可直接调用。

由于合约是在Chrome V8中执行的,因此所有实例变量都在内存中,所以将它们全部保存为state trie并不明智。在Nebulas中,我们提供LocalContractStorageGlobalContractStorage对象来帮助开发人员定义需要保存的状态字段。这些字段应该在合约构造函数中定义。

下面是一个简单的合约例子:

class Rectangle {
    constructor() {
        // define fields stored to state trie.
        LocalContractStorage.defineProperties(this, {
            height: null,
            width: null,
        });
    }

    // init function.
    init(height, width) {
        this.height = height;
        this.width = width;
    }

    // calc area function.
    calcArea() {
        return this.height * this.width;
    }

    // verify function.
    verify(expected) {
        let area = this.calcArea();
        if (expected != area) {
            throw new Error("Error: expected " + expected + ", actual is " + area + ".");
        }
    }
}
函数可见性

在JavaScript中,没有函数可见性,原型对象中定义的所有函数都是公有的。

在Nebulas中,我们定义了publicprivate两种可见性:

  • public 除init函数外,其他函数名与正则表达式 ^[a-zA-Z$][A-Za-z0-9_$]*$ 匹配的所有函数都是公有的。公有函数用户可直接调用。
  • private 函数名以_开头的函数都是私有的。私有函数只能通过公有函数调用。
全局对象
console

console模块提供了一个简单的调试控制台,与Web浏览器提供的JavaScript控制台机制类似。全局控制台可以在不调用require('console')的情况下使用。

console.info([...args])
  • ...args <any> console.info() 方法是console.log()的别名.
console.log([...args])
  • ...args <any>info级别输出args到Nebulas Logger.
console.debug([...args])
  • ...args <any>debug级别输出args到Nebulas Logger.
console.warn([...args])
  • ...args <any>warn级别输出args到Nebulas Logger.
console.error([...args])
  • ...args <any>error级别输出args到Nebulas Logger.
LocalContractStorage

LocalContractStorage模块提供基于状态树的存储功能。它仅接受字符串键值对。并且所有数据都存储到与当前合约地址关联的私有状态树中,只有合约可以访问它们。

BigNumber

BigNumber模块使用bignumber.js(v4.1.0),这是一个用于任意精度十进制和非十进制算术的JavaScript库。合约可以直接使用``BigNumber``来处理transaction值。

var value = new BigNumber(0);
value.plus(1);
...
Blockchain

Blockchain为合约提供了一个对象,该对象可以取得当前合约所在的块和Transaction信息,此外该对象提供了transfer方法用于从合约中转出nas,提供了verifyAddress用于地址校验。

Blockchain API:

// current block
Blockchain.block;

// current transaction, transaction's value/gasPrice/gasLimit auto change to BigNumber object
Blockchain.transaction;

// transfer NAS from contract to address
Blockchain.transfer(address, value);

// verify address
Blockchain.verifyAddress(address);

// get accout state
Blockchain.getAccountState(address);

// get previous block's hash
Blockchain.getPreBlockHash(offset);

// get previous block's random seed
Blockchain.getPreBlockSeed(offset);

properties:

  • block: 合约执行的当前块
    • timestamp: 块时间戳
    • seed: 随机数种子,从1.1.0开始返回""
    • height: 块高度
  • transaction: 合约执行的当前Transaction
    • hash: 交易哈希
    • from: 交易发送地址
    • to: 交易目的地址
    • value: 交易金额, 一个BigNumber对象
    • nonce: 交易nonce
    • timestamp: 交易时间戳
    • gasPrice: gas出价, 一个BigNumber对象
    • gasLimit: gas上限值, 一个BigNumber对象
  • transfer(address, value): 该函数将来自合约中的NAS发送到目的地址
    • 参数:
      • address: 接收NAS的nebulas地址
      • value: 交易金额,一个BigNumber/Uint(推荐使用)对象;单位为wei,所以只能是整数,用小数会失败。
    • 返回值(布尔型):
      • true: 交易成功
      • false: 交易失败
  • verifyAddress(address): 该函数校验地址
    • 参数:
      • address: 需要校验的地址
    • 返回值(数字型):
      • 87: 用户钱包地址
      • 88: 合约地址
      • 0: 地址非法
  • getAccountState(address)(testnet): 获取账户的余额和nonce
  • 参数:
    • address: 想要获取余额的地址
  • 返回值 (JSON 对象):
    • balance: 账户的余额
    • nonce: 账户的nonce
  • getPreBlockHash(offset)(testnet): 得到之前的块的哈希
  • 参数:
    • offset: 想要查询的块的高度和当前高度的偏移量。这个参数必须是整型,且大于0,小于当前高度。如果offset为1,表示上一个区块。
  • 返回值(string 类型):
    • hash: 区块哈希
  • getPreBlockSeed(offset)(testnet): 获取先前区块的随机种子
  • 参数:
    • offset: 和 Blockchain.getPreBlockHash() 中参数类似
  • 返回值(string 类型):
    • seed: 区块的随机种子

使用样例:

'use strict';

var SampleContract = function () {
    LocalContractStorage.defineProperties(this, {
        name: null,
        count: null
    });
    LocalContractStorage.defineMapProperty(this, "allocation");
};

SampleContract.prototype = {
    init: function (name, count, allocation) {
        this.name = name;
        this.count = count;
        allocation.forEach(function (item) {
            this.allocation.put(item.name, item.count);
        }, this);
        console.log('init: Blockchain.block.coinbase = ' + Blockchain.block.coinbase);
        console.log('init: Blockchain.block.hash = ' + Blockchain.block.hash);
        console.log('init: Blockchain.block.height = ' + Blockchain.block.height);
        console.log('init: Blockchain.transaction.from = ' + Blockchain.transaction.from);
        console.log('init: Blockchain.transaction.to = ' + Blockchain.transaction.to);
        console.log('init: Blockchain.transaction.value = ' + Blockchain.transaction.value);
        console.log('init: Blockchain.transaction.nonce = ' + Blockchain.transaction.nonce);
        console.log('init: Blockchain.transaction.hash = ' + Blockchain.transaction.hash);
    },
    transfer: function (address, value) {
        var result = Blockchain.transfer(address, value);
        console.log("transfer result:", result);
        Event.Trigger("transfer", {
            Transfer: {
                from: Blockchain.transaction.to,
                to: address,
                value: value
            }
        });
    },
    verifyAddress: function (address) {
         var result = Blockchain.verifyAddress(address);
        console.log("verifyAddress result:", result);
    },
  getAccountState: function (address) {
    var state = Blockchain.getAccountState(address);
    console.log("getAccountState result:", state);
  },
  getPreBlockHash: function (offset) {
    var hash = Blockchain.getPreBlockHash(offset);
    console.log("getPreBlockHash result", hash);
  },
  getPreBlockSeed: function (offset) {
    var seed = Blockchain.getPreBlockSeed(offset);
    console.log("getPreBlockSeed result", seed);
  }
};

module.exports = SampleContract;
Event

Event模块记录合约中的执行事件。记录的事件存储在链上的事件树中。用户可以使用`GetEventsByHash <rpc/README.html#geteventsbyhash>`__通过当前交易哈希来获取该事件。所有合约事件的topic都会在合约代码中指定的topic前加上前缀chain.contract.作为最终存储的topic。

Event.Trigger(topic, obj);
  • topic: user-defined topic
  • obj: JSON object

你可以在刚才的SampleContract智能合约例子中看到相关的使用样例。

Math.random
  • Math.random()返回一个浮点伪随机数,范围从0到1,但不包含1。典型用法是:
"use strict";

var BankVaultContract = function () {};

BankVaultContract.prototype = {

    init: function () {},

    game: function(subscript){

        var arr =[1,2,3,4,5,6,7,8,9,10,11,12,13];

        for(var i = 0;i < arr.length; i++){
            var rand = parseInt(Math.random()*arr.length);
            var t = arr[rand];
            arr[rand] =arr[i];
            arr[i] = t;
        }

        return arr[parseInt(subscript)];
    },
};
module.exports = BankVaultContract;
  • Math.random.seed(myseed)可以使用此方法重置随机种子。参数myseed必须是 string注意 从1.1.0开始此方法被移除。 ```js “use strict”;

var BankVaultContract = function () {};

BankVaultContract.prototype = {

init: function () {},

game:function(subscript, myseed){

    var arr =[1,2,3,4,5,6,7,8,9,10,11,12,13];

    console.log(Math.random());

    for(var i = 0;i < arr.length; i++){

        if (i == 8) {
            // reset random seed with `myseed`
            Math.random.seed(myseed);
        }

        var rand = parseInt(Math.random()*arr.length);
        var t = arr[rand];
        arr[rand] =arr[i];
        arr[i] = t;
    }
    return arr[parseInt(subscript)];
},

};

module.exports = BankVaultContract; ### Date  所有标准API均可使用。Date对象的Timezone固定为`UTC+0`,Locale固定为`en-US`。js “use strict”;

var BankVaultContract = function () {};

BankVaultContract.prototype = { init: function () {},

test: function(){
    var d = new Date();
    return d.toString();
}

};

module.exports = BankVaultContract; ```

提醒: * new Date()/Date.now()返回当前块的时间戳,单位为毫秒。

accept

该方法支持普通转账及转账到合约地址。to是智能合约地址,普通转账能成功的前提是合约声明了函数accept(),且该函数执行成功。 如果该转账是非普通转账,则它将被视为普通函数调用。 注:不用合约间用blockchain.transfer转账不会触发accept函数

"use strict";
var DepositeContent = function (text) {
    if(text){
            var o = JSON.parse(text);
            this.balance = new BigNumber(o.balance);//余额信息
            this.address = o.address;
    }else{
            this.balance = new BigNumber(0);
            this.address = "";
        }
};

DepositeContent.prototype = {
    toString: function () {
        return JSON.stringify(this);
    }
};

var BankVaultContract = function () {
    LocalContractStorage.defineMapProperty(this, "bankVault", {
        parse: function (text) {
            return new DepositeContent(text);
        },
        stringify: function (o) {
            return o.toString();
        }
    });
};

BankVaultContract.prototype = {
    init: function () {},

    save: function () {
        var from = Blockchain.transaction.from;
        var value = Blockchain.transaction.value;
        value = new BigNumber(value);
        var orig_deposit = this.bankVault.get(from);
        if (orig_deposit) {
                value = value.plus(orig_deposit.balance);
        }

        var deposit = new DepositeContent();
        deposit.balance = new BigNumber(value);
        deposit.address = from;
        this.bankVault.put(from, deposit);
    },

    accept:function(){
        this.save();
        Event.Trigger("transfer", {
            Transfer: {
                from: Blockchain.transaction.from,
                to: Blockchain.transaction.to,
                value: Blockchain.transaction.value,
            }
        });
    }

};
module.exports = BankVaultContract;
Uint

Uint库基于bignumber.js封装了4种无符号整型:Uint64Uint128Uint256Uint512

静态属性:

  • MaxValue: 实例对象,表示具体类型的最大有效值

实例方法:

  • div(o):求商
    • 参数:
      • o: 除数, 类型必须和被除数一致
    • 返回:运算结果,类型同被除数
  • pow(o):幂
    • 参数:
      • o: 指数, 类型必须和底数一致
    • 返回:运算结果,类型同底数
  • minus(o):减
    • 参数:
      • o: 减数, 类型必须和被减数一致
    • 返回:运算结果,类型同被减数
  • mod(o):取模
    • 参数:
      • o: 模数, 类型必须和被模数一致
    • 返回:运算结果,类型同被模数
  • mul(o):乘
    • 参数:
      • o: 乘数, 类型必须和被乘数一致
    • 返回:运算结果,类型同被乘数
  • plus(o):加
    • 参数:
      • o: 加数, 类型必须和被加数一致
    • 返回:运算结果,类型同被加数
  • cmp(o):大小比较
    • 参数:
      • o: 类型必须和this一致
    • 返回值:
      • 1: this 大于 o
      • 0: this 等于 o
      • -1: this 小于 o
  • isZero():零值判断
    • 返回值:
      • true: this 为 0
      • false: this 不为 0
  • toString(base): 以字符形式输出this
    • 参数:
      • base: 以2 ~ 64进制格式输出, 默认10进制

使用示例:

'use strict';

var Uint64 = Uint.Uint64;
// var Uint128 = Uint.Uint128;
// var Uint256 = Uint.Uint256;
// var Uint512 = Uint.Uint512;

var Contract = function() {};

Contract.prototype = {
    init: function() {},

    testUint64: function() {
        var a  = new Uint64(7);
        var b = new Uint64("2");

        return {
            'a+b': a.plus(b).toString(10),  // 9
            'a-b': a.minus(b).toString(10), // 5
            'a*b': a.mul(b).toString(10),   // 14
            'a/b': a.div(b).toString(10),   // 3
            'a%b': a.mod(b).toString(10),   // 1
            'a^b': a.pow(b).toString(10),   // 49
            'a>b': a.cmp(b) == 1,           // true
            'a==0': a.isZero(),             // false
            'a': a.toString(),              // 7
            'MaxUint64': Uint64.MaxValue.toString(16), // ffffffffffffffff
        };
    }
};

module.exports = Contract;
require

require用于加载那些NVM启动时没有装载的第三方库.

当前可用的第三方库有:

  • crypto.js

典型用法:

var crypto = require('crypto.js');
...
crypto

crypto库提供了常用的哈希函数,需要在合约中使用require显式加载。

APIs:

  • sha256(str)
    • 参数:
      • str: 大小写敏感字符串
    • 返回值:
      • 16进制字符串, 长度64
  • sha3256(str)
    • 参数:
      • str: 大小写敏感字符串
    • 返回值:
      • 16进制字符串, 长度64
  • ripemd160(str)
    • 参数:
      • str: 大小写敏感字符串
    • 返回值:
      • 16进制字符串, 长度40
  • md5(str)
    • 参数:
      • str: 大小写敏感字符串
    • 返回值:
      • 16进制字符串, 长度32
  • base64(str)
    • 参数:
      • str: 大小写敏感字符串
    • 返回值:
      • base64编码字符串
  • recoverAddress(alg, hash, sign): 使用公钥数据解码出签名钱包地址
    • 参数:
      • alg: 使用的签名算法,当前只有一个有效值1,表示使用的是secp256k1
      • hash: 被签名数据,为长度64的16进制字符串
      • sign: 使用私钥对hash签名得到的16进制字符串
    • 返回值:
      • 星云钱包地址,如果失败返回null

使用示例:

'use strict';

// explicitly require
var crypto = require('crypto.js');

var Contract = function() {};

Contract.prototype = {
    init: function() {},

    sha256: function(str) {
        // str='Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.'

        // return "a32d6d686968192663b9c9e21e6a3ba1ba9b2e288470c2f98b790256530933e0"
        return crypto.sha256(str);
    },

    sha3256: function(str) {
        // str='Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.'

        // return "564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b"
        return crypto.sha3256(str);
    },

    ripemd160: function(str) {
        // str='Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.'

        // return "4236aa9974eb7b9ddb0f7a7ed06d4bf3d9c0e386"
        return crypto.ripemd160(str);
    },

    md5: function(str) {
        // str='Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.'

        // return "9954125a33a380c3117269cff93f76a7"
        return crypto.md5(str);
    },

    base64: function(str) {
        // str='Nebulas is a next generation public blockchain, aiming for a continuously improving ecosystem.'

        // return "TmVidWxhcyBpcyBhIG5leHQgZ2VuZXJhdGlvbiBwdWJsaWMgYmxvY2tjaGFpbiwgYWltaW5nIGZvciBhIGNvbnRpbnVvdXNseSBpbXByb3ZpbmcgZWNvc3lzdGVtLg=="
        return crypto.base64(str);
    },

    recoverAddress: function(alg, hash, sign) {
        // alg = 1
        // hash = '564733f9f3e139b925cfb1e7e50ba8581e9107b13e4213f2e4708d9c284be75b'
        // sign = 'd80e282d165f8c05d8581133df7af3c7c41d51ec7cd8470c18b84a31b9af6a9d1da876ab28a88b0226707744679d4e180691aca6bdef5827622396751a0670c101'

        // return 'n1F8QbdnhqpPXDPFT2c9a581tpia8iuF7o2'
        return crypto.recoverAddress(alg, hash, sign);
    }
};

module.exports = Contract;
合约间调用(1.1.0开始支持,测试网)

我们在智能合约中提供了一个简单的方法来调用另一个智能合约,下面的示例展示了proxyKvStore如何使用合约kvStore提供服务。

proxyKvStore.js:

"use strict"

var proxyKvStore = function() {
};

proxyKvStore.prototype = {
    init: function() {
        //
    },

    save: function(address, key, value) {
        var kvStore  = new Blockchain.Contract(address);
        kvStore.value(20000000000000000).call("save", key, value);
    },

    get: function(address, key) {
        var kvStore = new Blockchain.Contract(address);
        return kvStore.call("get", key);
    },
}

module.exports = proxyKvStore;

kvStore.js:

"use strict";

var item = function(text) {
  if (text) {
    var obj = JSON.parse(text);
    this.key = obj.key;
    this.value = obj.value;
    this.author = obj.text;
  } else {
      this.key = "";
      this.author = "";
      this.value = "";
  }
};

item.prototype = {
  toString: function () {
    return JSON.stringify(this);
  }
};

var kvStore = function () {
    LocalContractStorage.defineMapProperty(this, "repo", {
        parse: function (text) {
            return new item(text);
        },
        stringify: function (o) {
            return o.toString();
        }
    });
};

kvStore.prototype = {
    init: function () {},

    save: function (key, value) {
        console.log("reach child contract");

        key = key.trim();
        value = value.trim();
        if (key === "" || value === ""){
            throw new Error("empty key / value");
        }
        if (value.length > 128 || key.length > 128){
            throw new Error("key / value exceed limit length")
        }

        var from = Blockchain.transaction.from;
        var item = this.repo.get(key);

        if (item){
            throw new Error("value has been taken");
        }

        item = new item();
        item.author = from;
        item.key = key;
        item.value = value;
        this.repo.put(key, item);
    },

    get: function (key) {
        key = key.trim();
        if ( key === "" ) {
            throw new Error("empty key")
        }
        return this.repo.get(key);
    },

    throwErr: function() {
        throw("err for test");
    }
};
module.exports = kvStore;

在上面的例子中,为了使用kvStore.js, 我们首先通过kvStore的合约地址创建一个合约对象:

var kvStore  = new Blockchain.Contract(address);

随后我们就可以通过这个对象来调用kvStore这个智能合约:

kvStore.value(2000000000000000000).call("save", key, value);

或者:

kvStore.call("save", key, value);

‘value’函数决定了多少nas会被从调用者智能合约转到被调用调用的智能合约。这个函数不是必须的,缺省值是0.

值的一提的是,在被调用合约的执行环境中,Blockchain.from是调用合约的地址,Blockchain.value的值则是由调用合约执行的’value’函数的参数决定的;被调用合约中如果throw错误,调用合约无法catch,所有修改的状态会被回滚。

标注:

  • 多级调用最多允许3阶调用,例如A->B->C。无法再调用D
    • 考虑到业务的扩展和功能的扩展,3级调用已经能解决问题。而多级调用涉及内存和gas的多级损耗,以及多级调用的性能损耗。根据测试的参数设置了3级调用。如果后期社区有更多的超越3级调用的需求,可以考虑放大多级调用数。
  • gas消耗
  • 合约代码插桩后本身的gas消耗值采用单阶调用一致消耗规则
  • 跨合约A调用B时,需要缴纳32000个gas
  • 当任意阶合约出现gas不足时,合约调用失败,回滚整个过程
  • mem限制
  • 单阶合约在被调用时,限制最大的消耗值为40M. 而多阶调用时,允许消耗的最大内存为40M.
  • 当任意合约出现mem不足时,合约调用失败,回滚整个过程
  • event
  • 每触发一次跨合约的调用会触发一次event. topic:chain.innerContract.例如A->B->C.会触发A->B和B->C的event。
NRC20
Abstract

The following standard allows for the implementation of a standard API for tokens within smart contracts. This standard provides basic functionality to transfer tokens, as well as allows tokens to be approved so they can be spent by another on-chain third party.

Motivation

A standard interface allows that a new token can be created by any application easily : from wallets to decentralized exchanges.

Methods
name

Returns the name of the token - e.g. "MyToken".

// returns string, the name of the token.
function name()
symbol

Returns the symbol of the token. E.g. “TK“.

// returns string, the symbol of the token 
function symbol()
decimals

Returns the number of decimals the token uses - e.g. 8, means to divide the token amount by 100000000 to get its user representation.

// returns number, the number of decimals the token uses
function decimals()
totalSupply

Returns the total token supply.

// returns string, the total token supply, the decimal value is decimals* total.
function totalSupply()
balanceOf

Returns the account balance of a address.

// returns string, the account balance of another account with address
function balanceOf(address)
transfer

Transfers value amount of tokens to address, and MUST fire the Transfer event. The function SHOULD throw if the from account balance does not have enough tokens to spend.

Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.

// returns `true`, if transfer success, else throw error
function transfer(address, value)
transferFrom

Transfers value amount of tokens from address from to address to, and MUST fire the Transfer event.

The transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD throw unless the from account has deliberately authorized the sender of the message via some mechanism.

Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event.

// returns `true`, if transfer success, else throw error
function transferFrom(from, to, value)
approve

Allows spender to withdraw from your account multiple times, up the currentValue to the value amount. If this function is called again it overwrites the current allowance with value.

NOTE: To prevent attack vectors, the user needs to give a previous approve value, and the default value that is not approve is 0.

// returns `true`, if approve success, else throw error
function approve(spender, currentValue, value)
allowance

Returns the amount which spender is still allowed to withdraw from owner.

// returns string, the value allowed to withdraw from `owner`.
function allowance(owner, spender)
Events
transferEvent

MUST trigger when tokens are transferred, including zero value transfers.

A token contract which creates new tokens SHOULD trigger a Transfer event with the from address set to totalSupply when tokens are created.

function transferEvent: function(status, from, to, value)
approveEvent

MUST trigger on any call to approve(spender, currentValue, value).

function approveEvent: function(status, from, spender, value)
Implementation
Example implementations are available at
'use strict';

var Allowed = function (obj) {
    this.allowed = {};
    this.parse(obj);
}

Allowed.prototype = {
    toString: function () {
        return JSON.stringify(this.allowed);
    },

    parse: function (obj) {
        if (typeof obj != "undefined") {
            var data = JSON.parse(obj);
            for (var key in data) {
                this.allowed[key] = new BigNumber(data[key]);
            }
        }
    },

    get: function (key) {
        return this.allowed[key];
    },

    set: function (key, value) {
        this.allowed[key] = new BigNumber(value);
    }
}

var StandardToken = function () {
    LocalContractStorage.defineProperties(this, {
        _name: null,
        _symbol: null,
        _decimals: null,
        _totalSupply: {
            parse: function (value) {
                return new BigNumber(value);
            },
            stringify: function (o) {
                return o.toString(10);
            }
        }
    });

    LocalContractStorage.defineMapProperties(this, {
        "balances": {
            parse: function (value) {
                return new BigNumber(value);
            },
            stringify: function (o) {
                return o.toString(10);
            }
        },
        "allowed": {
            parse: function (value) {
                return new Allowed(value);
            },
            stringify: function (o) {
                return o.toString();
            }
        }
    });
};

StandardToken.prototype = {
    init: function (name, symbol, decimals, totalSupply) {
        this._name = name;
        this._symbol = symbol;
        this._decimals = decimals || 0;
        this._totalSupply = new BigNumber(totalSupply).mul(new BigNumber(10).pow(decimals));

        var from = Blockchain.transaction.from;
        this.balances.set(from, this._totalSupply);
        this.transferEvent(true, from, from, this._totalSupply);
    },

    // Returns the name of the token
    name: function () {
        return this._name;
    },

    // Returns the symbol of the token
    symbol: function () {
        return this._symbol;
    },

    // Returns the number of decimals the token uses
    decimals: function () {
        return this._decimals;
    },

    totalSupply: function () {
        return this._totalSupply.toString(10);
    },

    balanceOf: function (owner) {
        var balance = this.balances.get(owner);

        if (balance instanceof BigNumber) {
            return balance.toString(10);
        } else {
            return "0";
        }
    },

    transfer: function (to, value) {
        value = new BigNumber(value);
        if (value.lt(0)) {
            throw new Error("invalid value.");
        }

        var from = Blockchain.transaction.from;
        var balance = this.balances.get(from) || new BigNumber(0);

        if (balance.lt(value)) {
            throw new Error("transfer failed.");
        }

        this.balances.set(from, balance.sub(value));
        var toBalance = this.balances.get(to) || new BigNumber(0);
        this.balances.set(to, toBalance.add(value));

        this.transferEvent(true, from, to, value);
    },

    transferFrom: function (from, to, value) {
        var spender = Blockchain.transaction.from;
        var balance = this.balances.get(from) || new BigNumber(0);

        var allowed = this.allowed.get(from) || new Allowed();
        var allowedValue = allowed.get(spender) || new BigNumber(0);
        value = new BigNumber(value);

        if (value.gte(0) && balance.gte(value) && allowedValue.gte(value)) {

            this.balances.set(from, balance.sub(value));

            // update allowed value
            allowed.set(spender, allowedValue.sub(value));
            this.allowed.set(from, allowed);

            var toBalance = this.balances.get(to) || new BigNumber(0);
            this.balances.set(to, toBalance.add(value));

            this.transferEvent(true, from, to, value);
        } else {
            throw new Error("transfer failed.");
        }
    },

    transferEvent: function (status, from, to, value) {
        Event.Trigger(this.name(), {
            Status: status,
            Transfer: {
                from: from,
                to: to,
                value: value
            }
        });
    },

    approve: function (spender, currentValue, value) {
        var from = Blockchain.transaction.from;

        var oldValue = this.allowance(from, spender);
        if (oldValue != currentValue.toString()) {
            throw new Error("current approve value mistake.");
        }

        var balance = new BigNumber(this.balanceOf(from));
        var value = new BigNumber(value);

        if (value.lt(0) || balance.lt(value)) {
            throw new Error("invalid value.");
        }

        var owned = this.allowed.get(from) || new Allowed();
        owned.set(spender, value);

        this.allowed.set(from, owned);

        this.approveEvent(true, from, spender, value);
    },

    approveEvent: function (status, from, spender, value) {
        Event.Trigger(this.name(), {
            Status: status,
            Approve: {
                owner: from,
                spender: spender,
                value: value
            }
        });
    },

    allowance: function (owner, spender) {
        var owned = this.allowed.get(owner);

        if (owned instanceof Allowed) {
            var spender = owned.get(spender);
            if (typeof spender != "undefined") {
                return spender.toString(10);
            }
        }
        return "0";
    }
};

module.exports = StandardToken;
NRC721
Abstract

A class of unique tokens. NRC721 is a free, open standard that describes how to build unique tokens on the Nebulas blockchain. While all tokens are fungible (every token is the same as every other token) in NRC20, NRC721 tokens are all unique.

Motivation

NRC721 defines a minimum interface a smart contract must implement to allow unique tokens to be managed, owned, and traded. It does not mandate a standard for token metadata or restrict adding supplemental functions.

Methods
name

Returns the name of the token - e.g. "MyToken".

// returns string, the name of the token.
function name()
balanceOf

Returns the number of tokens owned by owner.

// returns The number of NFTs owned by `owner`, possibly zero
function balanceOf(owner)
ownerOf

Returns the address of the owner of the tokens.

// returns the address of the owner of the tokens
function ownerOf(tokenId)
transferFrom

Transfers the ownership of an token from one address to another address. The caller is responsible to confirm that to is capable of receiving token or else they may be permanently lost.

Transfers tokenId tokenId from address from to address to, and MUST fire the Transfer event.

The function SHOULD throws unless the transaction from is the current owner, an authorized operator, or the approved address for this token. throws if from is not the current owner. throws if to is the contract address. throws if tokenId is not a valid token.

// if transfer fail, throw error
function transferFrom(from, to, tokenId)
approve

Set or reaffirm the approved address for an token.

The function SHOULD throws unless transcation from is the current token owner, or an authorized operator of the current owner.

function approve(to, tokenId)
setApprovalForAll

Enable or disable approval for a third party (operator) to manage all of transaction from‘s assets.

operator Address to add to the set of authorized operators. approved True if the operators is approved, false to revoke approval

function setApprovalForAll(operator, approved)
getApproved

Get the approved address for a single token.

// return the approved address for this token, or "" if there is none
function getApproved(tokenId)
isApprovedForAll

Query if an address is an authorized operator for another address.

// return true if `operator` is an approved operator for `owner`, false otherwise
function isApprovedForAll(owner, operator)
Events
_transferEvent

This emits when ownership of any token changes by any mechanism.

function _transferEvent: function(status, from, to, value)
_approveEvent

This emits when the approved address for an token is changed or reaffirmed.

When a Transfer event emits, this also indicates that the approved address for that token (if any) is reset to none

function _approveEvent: function(status, from, spender, value)
Implementation
Example implementations are available at
'use strict';

var Operator = function (obj) {
    this.operator = {};
    this.parse(obj);
};

Operator.prototype = {
    toString: function () {
        return JSON.stringify(this.operator);
    },

    parse: function (obj) {
        if (typeof obj != "undefined") {
            var data = JSON.parse(obj);
            for (var key in data) {
                this.operator[key] = data[key];
            }
        }
    },

    get: function (key) {
        return this.operator[key];
    },

    set: function (key, value) {
        this.operator[key] = value;
    }
};

var StandardToken = function () {
    LocalContractStorage.defineProperties(this, {
        _name: null,
    });

    LocalContractStorage.defineMapProperties(this, {
        "tokenOwner": null,
        "ownedTokensCount": {
            parse: function (value) {
                return new BigNumber(value);
            },
            stringify: function (o) {
                return o.toString(10);
            }
        },
        "tokenApprovals": null,
        "operatorApprovals": {
            parse: function (value) {
                return new Operator(value);
            },
            stringify: function (o) {
                return o.toString();
            }
        },
        
    });
};

StandardToken.prototype = {
    init: function (name) {
        this._name = name;
    },

    name: function () {
        return this._name;
    },

    // Returns the number of tokens owned by owner.
    balanceOf: function (owner) {
        var balance = this.ownedTokensCount.get(owner);
        if (balance instanceof BigNumber) {
            return balance.toString(10);
        } else {
            return "0";
        }
    },

    //Returns the address of the owner of the tokenID.
    ownerOf: function (tokenID) {
        return this.tokenOwner.get(tokenID);
    },

    /**
     * Set or reaffirm the approved address for an token.
     * The function SHOULD throws unless transcation from is the current token owner, or an authorized operator of the current owner.
     */
    approve: function (to, tokenId) {
        var from = Blockchain.transaction.from;

        var owner = this.ownerOf(tokenId);
        if (to == owner) {
            throw new Error("invalid address in approve.");
        }
        if (owner == from || this.isApprovedForAll(owner, from)) {
            this.tokenApprovals.set(tokenId, to);
            this._approveEvent(true, owner, to, tokenId);
        } else {
            throw new Error("permission denied in approve.");
        }
    },

    // Returns the approved address for a single token.
    getApproved: function (tokenId) {
        return this.tokenApprovals.get(tokenId);
    },

    /**
     * Enable or disable approval for a third party (operator) to manage all of transaction from's assets.
     * operator Address to add to the set of authorized operators. 
     * @param approved True if the operators is approved, false to revoke approval
     */
    setApprovalForAll: function(to, approved) {
        var from = Blockchain.transaction.from;
        if (from == to) {
            throw new Error("invalid address in setApprovalForAll.");
        }
        var operator = this.operatorApprovals.get(from) || new Operator();
        operator.set(to, approved);
        this.operatorApprovals.set(from, operator);
    },

    /**
     * @dev Tells whether an operator is approved by a given owner
     * @param owner owner address which you want to query the approval of
     * @param operator operator address which you want to query the approval of
     * @return bool whether the given operator is approved by the given owner
     */
    isApprovedForAll: function(owner, operator) {
        var operator = this.operatorApprovals.get(owner);
        if (operator != null) {
            if (operator.get(operator) === "true") {
                return true;
            } else {
                return false;
            }
        }
    },

    /**
     * @dev Returns whether the given spender can transfer a given token ID
     * @param spender address of the spender to query
     * @param tokenId uint256 ID of the token to be transferred
     * @return bool whether the msg.sender is approved for the given token ID,
     *  is an operator of the owner, or is the owner of the token
     */
    _isApprovedOrOwner: function(spender, tokenId) {
        var owner = this.ownerOf(tokenId);
        return spender == owner || this.getApproved(tokenId) == spender || this.isApprovedForAll(owner, spender);
    },

    /**
     * Transfers the ownership of an token from one address to another address. 
     * The caller is responsible to confirm that to is capable of receiving token or else they may be permanently lost.
     * Transfers tokenId from address from to address to, and MUST fire the Transfer event.
     * The function SHOULD throws unless the transaction from is the current owner, an authorized operator, or the approved address for this token. 
     * Throws if from is not the current owner. 
     * Throws if to is the contract address. 
     * Throws if tokenId is not a valid token.
     */
    transferFrom: function (from, to, tokenId) {
        var sender = Blockchain.transaction.from;
        var contractAddress = Blockchain.transaction.to;
        if (contractAddress == to) {
            throw new Error("Forbidden to transfer money to a smart contract address");
        }
        if (this._isApprovedOrOwner(sender, tokenId)) {
            this._clearApproval(from, tokenId);
            this._removeTokenFrom(from, tokenId);
            this._addTokenTo(to, tokenId);
            this._transferEvent(true, from, to, tokenId);
        } else {
            throw new Error("permission denied in transferFrom.");
        }
        
    },

     /**
     * Internal function to clear current approval of a given token ID
     * Throws if the given address is not indeed the owner of the token
     * @param sender owner of the token
     * @param tokenId uint256 ID of the token to be transferred
     */
    _clearApproval: function (sender, tokenId) {
        var owner = this.ownerOf(tokenId);
        if (sender != owner) {
            throw new Error("permission denied in clearApproval.");
        }
        this.tokenApprovals.del(tokenId);
    },

    /**
     * Internal function to remove a token ID from the list of a given address
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    _removeTokenFrom: function(from, tokenId) {
        if (from != this.ownerOf(tokenId)) {
            throw new Error("permission denied in removeTokenFrom.");
        }
        var tokenCount = this.ownedTokensCount.get(from);
        if (tokenCount.lt(1)) {
            throw new Error("Insufficient account balance in removeTokenFrom.");
        }
        this.ownedTokensCount.set(from, tokenCount.sub(1));
    },

    /**
     * Internal function to add a token ID to the list of a given address
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    _addTokenTo: function(to, tokenId) {
        this.tokenOwner.set(tokenId, to);
        var tokenCount = this.ownedTokensCount.get(to) || new BigNumber(0);
        this.ownedTokensCount.set(to, tokenCount.add(1));
    },

    /**
     * Internal function to mint a new token
     * @param to The address that will own the minted token
     * @param tokenId uint256 ID of the token to be minted by the msg.sender
     */
    _mint: function(to, tokenId) {
        this._addTokenTo(to, tokenId);
        this._transferEvent(true, "", to, tokenId);
    },

    /**
     * Internal function to burn a specific token
     * @param tokenId uint256 ID of the token being burned by the msg.sender
     */
    _burn: function(owner, tokenId) {
        this._clearApproval(owner, tokenId);
        this._removeTokenFrom(owner, tokenId);
        this._transferEvent(true, owner, "", tokenId);
    },

    _transferEvent: function (status, from, to, tokenId) {
        Event.Trigger(this.name(), {
            Status: status,
            Transfer: {
                from: from,
                to: to,
                tokenId: tokenId
            }
        });
    },

    _approveEvent: function (status, owner, spender, tokenId) {
        Event.Trigger(this.name(), {
            Status: status,
            Approve: {
                owner: owner,
                spender: spender,
                tokenId: tokenId
            }
        });
    }

};

module.exports = StandardToken;
工具

所有开发工具:来自社区的官方开发工具。您可以直接在Github上推荐更多工具并编辑此页面。

Full functions: web

Local NVM: Mac OS, Windows, Linux

Nebulas payment JavaScript API. 用户可以在PC和移动设备上的浏览器中使用它。 用户可以通过Chrome扩展程序和iOS / Android钱包进行NAS支付。

开发环境安装
JavaScript开发工具
DApp开发框架
合约部署工具
命令行工具
其他

NebulasDB是一款基于星云链、去中心化、非关系型的数据库, 并且提供了JS-SDK、客户端

RPC

Remote Procedure Calls (RPCs) provide a useful abstraction for building distributed applications and services.

Nebulas provides both gRPC and RESTful API for users to interact with Nebulas.

grpc provides a concrete implementation of the gRPC protocol, layered over HTTP/2. These libraries enable communication between clients and servers using any combination of the supported languages.

grpc-gateway is a plugin of protoc. It reads gRPC service definition, and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. We use it to map gRPC to HTTP.

Endpoint

Default endpoints:

API URL Protocol
gRPC http://localhost:8684 Protobuf
RESTful http://localhost:8685 HTTP
gRPC API

We can run the gRPC example testing client code:

go run main.go

The testing client gets account state from sender address, makes a transaction from sender to receiver, and also checks the account state of receiver address.

We can see client log output like:

GetAccountState n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5 nonce 4 value 3142831039999999999992
SendTransaction n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5 -> n1Zn6iyyQRhqthmCfqGBzWfip1Wx8wEvtrJ value 2 txhash:"2c2f5404a2e2edb651dff44a2d114a198c00614b20801e58d5b00899c8f512ae"
GetAccountState n1Zn6iyyQRhqthmCfqGBzWfip1Wx8wEvtrJ nonce 0 value 10
HTTP

We have also provided HTTP to access the RPC API. The file that ends with gw.go is the mapping file. Now we can access the rpc API directly from our browser, you can update the rpc_listen and http_listen in conf/default/config.conf to change the RPC/HTTP ports, respectively.

Example:

curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/user/nebstate

if successful, response will be returned like this

{
    "result":{
        "chain_id":100,
        "tail":"b10c1203d5ae6d4d069d5f520eb060f2f5fb74e942f391e7cadbc2b5148dfbcb",
        "lib":"da30b4ed14affb62b3719fb5e6952d3733e84e53fe6e955f8e46da503300c985",
        "height":"365",
        "protocol_version":"/neb/1.0.0",
        "synchronized":false,
        "version":"0.7.0"
    }
}

Or, there is an error from gRPC, and the reponse will carry the error message.

{
    "error":"message..."
}
RPC API Reference
GetNebState

Return the state of the neb.

Protocol Method API
gRpc   GetNebState
HTTP GET /v1/user/nebstate

Parameters

none

Returns

chain_id Block chain id: * 1: mainnet.

  • 1001: testnet.

tail current neb tail hash.

lib current neb lib hash.

height current neb tail block height.

protocol_version current neb protocol version.

synchronized peer sync status.

version neb version.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/user/nebstate

// Result
{
    "result":{
        "chain_id":100,
        "tail":"b10c1203d5ae6d4d069d5f520eb060f2f5fb74e942f391e7cadbc2b5148dfbcb",
        "lib":"da30b4ed14affb62b3719fb5e6952d3733e84e53fe6e955f8e46da503300c985",
        "height":"365",
        "protocol_version":"/neb/1.0.0",
        "synchronized":false,
        "version":"0.7.0"
    }
}

GetAccountState

Return the state of the account. Balance and nonce of the given address will be returned.

Protocol Method API
gRpc   GetAccountState
HTTP POST /v1/user/accountstate

Parameters

address Hex string of the account addresss.

height block account state with height. If not specified, use 0 as tail height.

Returns

balance Current balance in unit of 1/(10^18) nas.

nonce Current transaction count.

type The type of address, 87 stands for normal address and 88 stands for contract address.

height Current height of blockchain.

pending pending transactions of address in Tx pool.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/accountstate -d '{"address":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3"}'

// Result
{
    result {
        "balance":"9489999998980000000000"
        "nonce":51
        "type":87
        "height":"100",
        "pending":"0"
    }
}

LatestIrreversibleBlock

Return the latest irreversible block.

Protocol Method API
gRpc   LatestIrreversibleBlock
HTTP GET /v1/user/lib

Parameters

none

Returns

hash Hex string of block hash.

parent_hash Hex string of block parent hash.

height block height.

nonce block nonce.

coinbase Hex string of coinbase address.

timestamp block timestamp.

chain_id block chain id.

state_root Hex string of state root.

txs_root Hex string of txs root.

events_root Hex string of event root.

consensus_root

  • Timestamp time of consensus state.

  • Proposer proposer of current consensus state.

  • DynastyRoot Hex string of dynasty root.

    miner the miner of this block.

    is_finality block is finality.

    transactions block transactions slice.

  • transaction GetTransactionReceipt response info.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/user/lib

// Result
{
    "result":{
        "hash":"c4a51d6241db372c1b8720e62c04426bd587e1f31054b7d04a3509f48ee58e9f",
        "parent_hash":"8f9f29028356d2fb2cf1291dcee85785e1c20a2145318f36c136978edb6097ce",
        "height":"407",
        "nonce":"0",
        "coinbase":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
        "timestamp":"1521963660",
        "chain_id":100,
        "state_root":"a77bbcd911e7ee9488b623ce4ccb8a38d9a83fc29eb5ad43009f3517f1d3e19a",
        "txs_root":"664671e2fda200bd93b00aaec4ab12db718212acd51b4624e8d4937003a2ab22",
        "events_root":"2607e32c166a3513f9effbd1dc7caa7869df5989398d0124987fa0e4d183bcaf",
        "consensus_root":{
            "timestamp":"1521963660",
            "proposer":"GVeOQnYf20Ppxa2cqTrPHdpr6QH4SKs4ZKs=",
            "dynasty_root":"IfTgx0o271Gg4N3cVKHe7dw3NREnlYCN8aIl8VvRXDY="
        },
        "miner": "n1WwqBXVMuYC3mFCEEuFFtAXad6yxqj4as4"
        "is_finality":false,
        "transactions":[]
    }
}

Call

Call a smart contract function. The smart contract must have been submited. Method calls are run only on the current node, not broadcast.

Protocol Method API
gRpc   Call
HTTP POST /v1/user/call

Parameters

The parameters of the call method are the same as the SendTransaction parameters. Special attention:

to Hex string of the receiver account addresss. The value of ``to`` is a contract address.

contract transaction contract object for call smart contract.

  • Sub properties(``source`` and ``sourceType`` are not need):
  • function the contract call function for call contract function.
  • args the params of contract. The args content is JSON string of parameters array.

Returns

result result of smart contract method call.

execute_err execution error.

estimate_gas estimate gas used.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/call -d '{"from":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3","to":"n1mL2WCZyRi1oELEugfCZoNAW3dt8QpHtJw","value":"0","nonce":3,"gasPrice":"20000000000","gasLimit":"2000000","contract":{"function":"transferValue","args":"[500]"}}'

// Result
{
   "result": {
       "result": "0",
       "execute_err": "insufficient balance",
       "estimate_gas": "22208"
   }
}

SendRawTransaction

Submit the signed transaction. The transaction signed value should be return by SignTransactionWithPassphrase.

Protocol Method API
gRpc   SendRawTransaction
HTTP POST /v1/user/rawtransaction

Parameters

data Signed data of transaction

Returns

txhash Hex string of transaction hash.

contract_address returns only for deployed contract transaction.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/rawtransaction -d '{"data":"CiCrHtxyyIJks2/RErvBBA862D6iwAaGQ9OK1NisSGAuTBIYGiY1R9Fnx0z0uPkWbPokTeBIHFFKRaosGhgzPLPtjEF5cYRTgu3jz2egqWJwwF/i9wAiEAAAAAAAAAAADeC2s6dkAAAoAjDd/5jSBToICgZiaW5hcnlAZEoQAAAAAAAAAAAAAAAAAA9CQFIQAAAAAAAAAAAAAAAAAABOIFgBYkGLnnvGZEDSlocc202ZRWtUlbl2RHfGNdBY5eajFiHKThfgXIwGixh17LpnZGnYHlmfiGe2zqnFHdj7G8b2XIP2AQ=="}'

// Result
{
    "result":{
        "txhash": "f37acdf93004f7a3d72f1b7f6e56e70a066182d85c186777a2ad3746b01c3b52"
    }
}

Deploy Contract Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/rawtransaction -d '{"data":"CiDam3G9Sy5fV6/ZcjasYPwSF39ZJDIHNB0Us94vn6p6ohIaGVfLzJ83pom1DO1gD307f1JdTVdDLzbMXO4aGhlXy8yfN6aJtQztYA99O39SXU1XQy82zFzuIhAAAAAAAAAAAAAAAAAAAAAAKBswwfTs1QU64AcKBmRlcGxveRLVB3siU291cmNlVHlwZSI6ImpzIiwiU291cmNlIjoiJ3VzZSBzdHJpY3QnXG5cbnZhciBUcmFuc2ZlclZhbHVlQ29udHJhY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgLy8gTG9jYWxDb250cmFjdFN0b3JnZS5kZWZpbmVQcm9wZXJ0aWVzKHRoaXMsIHtcbiAgICAvLyAgICAgdG90YWxCYWxhbmNlOiBudWxsXG4gICAgLy8gfSlcbn1cblxuXG5UcmFuc2ZlclZhbHVlQ29udHJhY3QucHJvdG90eXBlID0ge1xuICAgICBpbml0OiBmdW5jdGlvbigpIHtcbiAgICAvLyAgICAgdGhpcy50b3RhbEJhbGFuY2UgPSAwO1xuICAgICB9LFxuXG4gICAgdHJhbnNmZXI6IGZ1bmN0aW9uKHRvKSB7XG4gICAgICAgIHZhciByZXN1bHQgPSBCbG9ja2NoYWluLnRyYW5zZmVyKHRvLCBCbG9ja2NoYWluLnRyYW5zYWN0aW9uLnZhbHVlKTtcbiAgICAgICAgLy8gdmFyIHJlc3VsdCA9IEJsb2NrY2hhaW4udHJhbnNmZXIodG8sIDApO1xuICAgICAgICBpZiAoIXJlc3VsdCkge1xuXHQgICAgXHR0aHJvdyBuZXcgRXJyb3IoXCJ0cmFuc2ZlciBmYWlsZWQuXCIpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBCbG9ja2NoYWluLnRyYW5zYWN0aW9uLnZhbHVlO1xuICAgIH0sXG4gICAgdHJhbnNmZXJTcGVjaWFsVmFsdWU6IGZ1bmN0aW9uKHRvLCB2YWx1ZSkge1xuICAgICAgICB2YXIgYW1vdW50ID0gbmV3IEJpZ051bWJlcih2YWx1ZSk7XG4gICAgICAgIHZhciByZXN1bHQgPSBCbG9ja2NoYWluLnRyYW5zZmVyKHRvLCBhbW91bnQpO1xuICAgICAgICAvLyB2YXIgcmVzdWx0ID0gQmxvY2tjaGFpbi50cmFuc2Zlcih0bywgMCk7XG4gICAgICAgIGlmICghcmVzdWx0KSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJ0cmFuc2ZlciBmYWlsZWQuXCIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIDBcbiAgICAgICAgfVxuICAgIH0sXG4gICAgXG59XG5tb2R1bGUuZXhwb3J0cyA9IFRyYW5zZmVyVmFsdWVDb250cmFjdDsifUBkShAAAAAAAAAAAAAAAAAAD0JAUhAAAAAAAAAAAAAAAAABMS0AWAFiQcJUX32jGcduxnJCjvJ9kRcGXhSK2+h3Tb46ySjAToGAY11C7mysGEU11OE6YTd+WNAo/CEbThvI0iKcjHhgBZUB"}'

// Result
{
    "result":{
        "txhash": "f37acdf93004f7a3d72f1b7f6e56e70a066182d85c186777a2ad3746b01c3b52",
        "contract_address":"4702b597eebb7a368ac4adbb388e5084b508af582dadde47"
    }
}

GetBlockByHash

Get block header info by the block hash.

Protocol Method API
gRpc   GetBlockByHash
HTTP POST /v1/user/getBlockByHash

Parameters

hash Hex string of block hash.

full_fill_transaction If true it returns the full transaction objects, if false only the hashes of the transactions.

Returns

See LatestIrreversibleBlock response.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getBlockByHash -d '{"hash":"c4a51d6241db372c1b8720e62c04426bd587e1f31054b7d04a3509f48ee58e9f", "full_fill_transaction":true}'

// Result
{
    "result":{
        "hash":"c4a51d6241db372c1b8720e62c04426bd587e1f31054b7d04a3509f48ee58e9f",
        "parent_hash":"8f9f29028356d2fb2cf1291dcee85785e1c20a2145318f36c136978edb6097ce",
        "height":"407",
        "nonce":"0",
        "coinbase":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
        "timestamp":"1521963660",
        "chain_id":100,
        "state_root":"a77bbcd911e7ee9488b623ce4ccb8a38d9a83fc29eb5ad43009f3517f1d3e19a",
        "txs_root":"664671e2fda200bd93b00aaec4ab12db718212acd51b4624e8d4937003a2ab22",
        "events_root":"2607e32c166a3513f9effbd1dc7caa7869df5989398d0124987fa0e4d183bcaf",
        "consensus_root":{
            "timestamp":"1521963660",
            "proposer":"GVeOQnYf20Ppxa2cqTrPHdpr6QH4SKs4ZKs=",
            "dynasty_root":"IfTgx0o271Gg4N3cVKHe7dw3NREnlYCN8aIl8VvRXDY="
        },
        "miner": "n1WwqBXVMuYC3mFCEEuFFtAXad6yxqj4as4"
        "is_finality":false,
        "transactions":[{
            "hash":"1e96493de6b5ebe686e461822ec22e73fcbfb41a6358aa58c375b935802e4145",
            "chainId":100,
            "from":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
            "to":"n1orSeSMj7nn8KHHN4JcQEw3r52TVExu63r",
            "value":"10000000000000000000","nonce":"34",
            "timestamp":"1522220087",
            "type":"binary",
            "data":null,
            "gas_price":"1000000",
            "gas_limit":"2000000",
            "contract_address":"",
            "status":1,
            "gas_used":"20000"
        }]
    }
}

GetBlockByHeight

Get block header info by the block height.

Protocol Method API
gRpc   GetBlockByHeight
HTTP POST /v1/user/getBlockByHeight

Parameters

height Height of transaction hash.

full_fill_transaction If true it returns the full transaction objects, if false only the hashes of the transactions.

Returns

See LatestIrreversibleBlock response.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getBlockByHeight -d '{"height": 256, "full_fill_transaction": true}'

// Result
{
    "result":{
        "hash":"c4a51d6241db372c1b8720e62c04426bd587e1f31054b7d04a3509f48ee58e9f",
        "parent_hash":"8f9f29028356d2fb2cf1291dcee85785e1c20a2145318f36c136978edb6097ce",
        "height":"407",
        "nonce":"0",
        "coinbase":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
        "timestamp":"1521963660",
        "chain_id":100,
        "state_root":"a77bbcd911e7ee9488b623ce4ccb8a38d9a83fc29eb5ad43009f3517f1d3e19a",
        "txs_root":"664671e2fda200bd93b00aaec4ab12db718212acd51b4624e8d4937003a2ab22",
        "events_root":"2607e32c166a3513f9effbd1dc7caa7869df5989398d0124987fa0e4d183bcaf",
        "consensus_root":{
            "timestamp":"1521963660",
            "proposer":"GVeOQnYf20Ppxa2cqTrPHdpr6QH4SKs4ZKs=",
            "dynasty_root":"IfTgx0o271Gg4N3cVKHe7dw3NREnlYCN8aIl8VvRXDY="
        },
        "miner": "n1WwqBXVMuYC3mFCEEuFFtAXad6yxqj4as4"
        "is_finality":false,
        "transactions":[{
            "hash":"1e96493de6b5ebe686e461822ec22e73fcbfb41a6358aa58c375b935802e4145",
            "chainId":100,
            "from":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
            "to":"n1orSeSMj7nn8KHHN4JcQEw3r52TVExu63r",
            "value":"10000000000000000000","nonce":"34",
            "timestamp":"1522220087",
            "type":"binary",
            "data":null,
            "gas_price":"1000000",
            "gas_limit":"2000000",
            "contract_address":"",
            "status":1,
            "gas_used":"20000"
        }]
    }
}

GetTransactionReceipt

Get transactionReceipt info by transaction hash. If the transaction is not submitted or only submitted but is not packaged on chain, it will return “not found” error.

Protocol Method API
gRpc   GetTransactionReceipt
HTTP POST /v1/user/getTransactionReceipt

Parameters

hash Hex string of transaction hash.

Returns

hash Hex string of tx hash.

chainId Transaction chain id.

from Hex string of the sender account addresss.

to Hex string of the receiver account addresss.

value Value of transaction.

nonce Transaction nonce.

timestamp Transaction timestamp.

type Transaction type.

data Transaction data, return the payload data.

gas_price Transaction gas price.

gas_limit Transaction gas limit.

contract_address Transaction contract address.

status Transaction status, 0 - failed, 1 - success, 2 - pending.

gas_used transaction gas used

execute_error the execution error of this transaction

execute_result return value of the smart-contract function

Note: the data length of execute_result is limited to 255 Bytes, if you want to receive a large return value from you smart-contract, please use api call instead.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getTransactionReceipt -d '{"hash":"cda54445ffccf4ea17f043e86e54be11b002053f9edbe30ae1fbc0437c2b6a73"}'

// Result
{
    "result":{
        "hash":"cda54445ffccf4ea17f043e86e54be11b002053f9edbe30ae1fbc0437c2b6a73",
        "chainId":100,
        "from":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
        "to":"n1PxKRaJ5jZHXwTfgM9WqkZJJVXBxRcggEE",
        "value":"10000000000000000000",
        "nonce":"53",
        "timestamp":"1521964742",
        "type":"binary",
        "data":null,
        "gas_price":"1000000",
        "gas_limit":"20000",
        "contract_address":"",
        "status":1,
        "gas_used":"20000",
        "execute_error":"",
        "execute_result":"\"\""
    }
}

GetTransactionByContract

Get transactionReceipt info by contract address. If contract does not exist or is not packaged on chain, a “not found” error will be returned.

Protocol Method API
gRpc   GetTransactionByContract
HTTP POST /v1/user/getTransactionByContract

Parameters

address Hex string of contract account address.

Returns

The result is the same as that of GetTransactionReceipt

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getTransactionByContract -d '{"address":"n1sqDHGjYtX6rMqFoq5Tow3s3LqF4ZxBvE3"}'

// Result
{
    "result":{
        "hash":"c5a45a789278f5cce9e95e8f31c1962567f58844456fed7a6eb9afcb764ca6a3",
        "chainId":100,
        "from":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
        "to":"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
        "value":"0",
        "nonce":"1",
        "timestamp":"1521964742",
        "type":"deploy",
        "data":"eyJTb3VyY2VUeXBlIjoianMiLCJTb3VyY2UiOiJcInVzZSBzdHJpY3RcIjtcblxudmFyIENvbnRyYWN0ID0gZnVuY3VuY3Rpb24oKSB7XG5cbiAgICAgICAgRXZlbnQuVHJpZ2dlcih......UmFuZG9tMlwiOiByMTIsXG4gImRlZmF1bHRTZWVkUmFuZG9tM1wiOiByMTMsXG4gICAgICAgICAgICBcInVzZXJTZWVkUmFuZG9tXCI6IHIyXG4gICAgICAgIH0pO1xuICAgIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0gQ29udHJhY3Q7IiwiQXJncyI6IiJ9",
        "gas_price":"1000000",
        "gas_limit":"20000",
        "contract_address":"n1sqDHGjYtX6rMqFoq5Tow3s3LqF4ZxBvE3",
        "status":1,
        "gas_used":"20000",
        "execute_error":"",
        "execute_result":"\"\""
    }
}

Subscribe

Return the subscribed events of transaction & block. The request is a keep-alive connection.

Note that subscribe doesn’t guarantee all new events will be received successfully, it depends on the network condition. Please run a local node to use subscribe api.

Protocol Method API
gRpc   Subscribe
HTTP POST /v1/user/subscribe

Parameters

topics repeated event topic name, string array.

The topic name list:

  • chain.pendingTransaction The topic of pending a transaction in transaction_pool.
  • chain.latestIrreversibleBlock The topic of updating latest irreversible block.
  • chain.transactionResult The topic of executing & submitting tx.
  • chain.newTailBlock The topic of setting new tail block.
  • chain.revertBlock The topic of reverting block.

Returns

topic subscribed event topic name.

data subscribed event data.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/subscribe -d '{"topics":["chain.linkBlock", "chain.pendingTransaction"]}'

// Result
{
    "result":{
        "topic":"chain.pendingTransaction",
        "data":"{
                \"chainID\":100,
                 \"hash\":\"b466c7a9b667db8d15f74863a4bc60bc989566b6c3766948b2cacb45a4fbda42\",
                 \"from\":\"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3\",
                 \"to\":\"n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3\",
                 \"nonce\":6,
                 \"value\":\"0\",
                 \"timestamp\":1522215320,
                 \"gasprice\": \"20000000000\",
                 \"gaslimit\":\"20000000\",
                 \"type\":\"deploy\"}"
    }
    "result":{
        "topic":"chain.pendingTransaction",
        "data": "..."
    }
    ...
}

GetGasPrice

Return current gasPrice.

Protocol Method API
gRpc   GetGasPrice
HTTP GET /v1/user/getGasPrice

Parameters

none

Returns

gas_price gas price. The unit is 10^-18 NAS.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/user/getGasPrice

// Result
{
    "result":{
        "gas_price":"20000000000"
    }
}

EstimateGas

Return the estimate gas of transaction.

Protocol Method API
gRpc   EstimateGas
HTTP POST /v1/user/estimateGas

Parameters

The parameters of the EstimateGas method are the same as the SendTransaction parameters.

Returns

gas the estimate gas.

err error message of the transaction being executed.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/estimateGas -d '{"from":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","to":"n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17", "value":"1000000000000000000","nonce":1,"gasPrice":"20000000000","gasLimit":"2000000"}'

// Result
{
    "result": {
        "gas":"20000",
        "err":""
    }
}

GetEventsByHash

Return the events list of transaction.

Protocol Method API
gRpc   GetEventsByHash
HTTP POST /v1/user/getEventsByHash

Parameters

hash Hex string of transaction hash.

Returns

events the events list. - topic event topic; - data event data.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/getEventsByHash -d '{"hash":"ec239d532249f84f158ef8ec9262e1d3d439709ebf4dd5f7c1036b26c6fe8073"}'

// Result
{
    "result":{
        "events":[{
            "topic":"chain.transactionResult",
            "data":"{
                \"hash\":\"d7977f96294cd232781d9c17f0f3212b48312d5ef0f556551c5cf48622759785\",
                \"status\":1,
                \"gas_used\":\"22208\",
                \"error\":\"\"
            }"
        }]
    }
}

GetDynasty

GetDynasty get dpos dynasty.

Protocol Method API
gRpc   GetDynasty
HTTP POST /v1/user/dynasty

Parameters

height block height

Returns

miners repeated string of miner address.

HTTP Example

// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/user/dynasty -d '{"height": 1}'

// Result
{
    {
        "result":{
            "miners":[
                "n1FkntVUMPAsESuCAAPK711omQk19JotBjM",
                "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR",
                "n1Kjom3J4KPsHKKzZ2xtt8Lc9W5pRDjeLcW",
                "n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC",
                "n1WwqBXVMuYC3mFCEEuFFtAXad6yxqj4as4",
                "n1Zn6iyyQRhqthmCfqGBzWfip1Wx8wEvtrJ"
            ]
        }
    }
}

Next Step:

管理RPC

Beside the NEB API RPC interface nebulas provides additional management APIs. Neb console supports both API and management interfaces. Management RPC uses the same gRPC and HTTP port, which also binds NEB API RPC interfaces.

Nebulas provide both gRPC and RESTful management APIs for users to interact with Nebulas. Our admin proto file defines all admin APIs. We recommend using the console access admin interfaces, or restricting the admin RPC to local access.

Default management RPC Endpoint:

API URL Protocol
gRPC http://localhost:8684 Protobuf
RESTful http://localhost:8685 HTTP
Management RPC API Reference
NodeInfo

Return the p2p node info.

Protocol Method API
gRpc NodeInfo
HTTP GET /v1/admin/nodeinfo
Parameters

none

Returns

id the node ID.

chain_id the block chainID.

coninbase coinbase

peer_count Number of peers currently connected.

synchronized the node synchronization status.

bucket_size the node route table bucket size.

protocol_version the network protocol version.

RouteTable*[] route_table the network routeTable

message RouteTable {
    string id = 1;
    repeated string address = 2;
}
HTTP Example
// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/admin/nodeinfo

// Result
{
    "result":{
        "id":"QmP7HDFcYmJL12Ez4ZNVCKjKedfE7f48f1LAkUc3Whz4jP",
        "chain_id":100,
        "coinbase":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
        "peer_count":4,
        "synchronized":false,
        "bucket_size":64,
        "protocol_version":"/neb/1.0.0",
        "route_table":[
            {
                "id":"QmP7HDFcYmJL12Ez4ZNVCKjKedfE7f48f1LAkUc3Whz4jP",
                "address":[
                    "/ip4/127.0.0.1/tcp/8680",
                    "/ip4/192.168.1.206/tcp/8680"
                ]
            },
            {
                "id":"QmUxw4PZ8kMEnHD8WaSVE92dtvdnwgufM6m5DrWemdk2M7",
                "address":[
                    "/ip4/192.168.1.206/tcp/10003","/ip4/127.0.0.1/tcp/10003"
                ]
            }
        ]
    }
}

Accounts

Return account list.

Protocol Method API
gRpc Accounts
HTTP GET /v1/admin/accounts
Parameters

none

Returns

addresses account list

HTTP Example
// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/admin/accounts

// Result
{
    "result":{
        "addresses":[
            "n1FkntVUMPAsESuCAAPK711omQk19JotBjM",
            "n1JNHZJEUvfBYfjDRD14Q73FX62nJAzXkMR",
            "n1Kjom3J4KPsHKKzZ2xtt8Lc9W5pRDjeLcW",
            "n1NHcbEus81PJxybnyg4aJgHAaSLDx9Vtf8",
            "n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
            "n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC",
            "n1WwqBXVMuYC3mFCEEuFFtAXad6yxqj4as4",
            "n1Z6SbjLuAEXfhX1UJvXT6BB5osWYxVg3F3",
            "n1Zn6iyyQRhqthmCfqGBzWfip1Wx8wEvtrJ"
        ]
    }
}

NewAccount

NewAccount create a new account with passphrase.

Protocol Method API
gRpc NewAccount
HTTP POST /v1/admin/account/new
Parameters

passphrase New account passphrase.

Returns

address New Account address.

HTTP Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/new -d '{"passphrase":"passphrase"}'

// Result

{
    "result":{
        "address":"n1czGUvbQQton6KUWga4wKDLLKYDEn39mEk"
    }
}

UnLockAccount

UnlockAccount unlock account with passphrase. After the default unlock time, the account will be locked.

Protocol Method API
gRpc UnLockAccount
HTTP POST /v1/admin/account/unlock
Parameters

address UnLock account address.

passphrase Unlock account passphrase.

duration Unlock account duration. The unit is ns (10e-9 s).

Returns

result UnLock account result, unit is ns.

HTTP Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/unlock -d '{"address":"n1czGUvbQQton6KUWga4wKDLLKYDEn39mEk","passphrase":"passphrase","duration":"1000000000"}'

// Result
{
    "result":{
        "result":true
    }
}

LockAccount

LockAccount lock account.

Protocol Method API
gRpc LockAccount
HTTP POST /v1/admin/account/lock
Parameters

address Lock account address.

Returns

result Lock account result.

HTTP Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/account/lock -d '{"address":"n1czGUvbQQton6KUWga4wKDLLKYDEn39mEk"}'

// Result
{
    "result":{
        "result":true
    }
}

SignTransactionWithPassphrase

SignTransactionWithPassphrase sign transaction. The transaction‘s from address must be unlocked before the ‘sign‘ call.

Protocol Method API
gRpc SignTransactionWithPassphrase
HTTP POST /v1/admin/sign
Parameters

transaction this is the same as the SendTransaction parameters.

passphrase from account passphrase

Returns

data Signed transaction data.

sign normal transaction Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/sign -d '{"transaction":{"from":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":1,"gasPrice":"1000000","gasLimit":"2000000"}, "passphrase":"passphrase"}'

// Result
{
    "result":{
        "data":"CiBOW15yoZ+XqQbMNr4bQdJCXrBTehJKukwjcfW5eASgtBIaGVduKnw+6lM3HBXhJEzzuvv3yNdYANelaeAaGhlXbip8PupTNxwV4SRM87r798jXWADXpWngIhAAAAAAAAAAAA3gtrOnZAAAKAEwucHt1QU6CAoGYmluYXJ5QGRKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAAehIBYAWJB/BwhwhqUkp/gEJtE4kndoc7NdSgqD26IQqa0Hjbtg1JaszAvHZiW+XH7C+Ky9XTKRJKuTOc446646d/Sbz/nxQE="
    }
}

SendTransactionWithPassphrase

SendTransactionWithPassphrase send transaction with passphrase.

Protocol Method API
gRpc SendTransactionWithPassphrase
HTTP POST /v1/admin/transactionWithPassphrase
Parameters

transaction transaction parameters, which are the same as the SendTransaction parameters.

passphrase from address passphrase.

Returns

txhash transaction hash.

contract_address returns only for deployed contract transaction.

Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transactionWithPassphrase -d '{"transaction":{"from":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"1000000000000000000","nonce":1,"gasPrice":"1000000","gasLimit":"2000000"},"passphrase":"passphrase"}'

// Result
{
    "result":{
        "hash":"143eac221da8079f017bd6fd6b6a08ea0623114c93c638b94334d16aae109666",
        "contract_address":""
    }
}

SendTransaction

Send the transaction. Parameters from, to, value, nonce, gasPrice and gasLimit are required. If the transaction is to send contract, you must specify the contract.

Protocol Method API
gRpc SendTransaction
HTTP POST /v1/admin/transaction
Parameters

from Hex string of the sender account addresss.

to Hex string of the receiver account addresss.

value Amount of value sending with this transaction. The unit is Wei (10^-18 NAS).

nonce Transaction nonce.

gas_price gasPrice sending with this transaction.

gas_limit gasLimit sending with this transaction.

type transaction payload type. If the type is specified, the transaction type is determined and the corresponding parameter needs to be passed in, otherwise the transaction type is determined according to the contract and binary data. [optional]

  • type enum:
    • binary: normal transaction with binary
    • deploy: deploy smart contract
    • call: call smart contract function

contract transaction contract object for deployed/calling smart contract. [optional]

  • Sub properties:
    • source contract source code for deployed contract.
    • sourceType contract source type for deployed contract. Currently support js and ts
      • js the contract source written with javascript.
      • ts the contract source written with typescript.
    • function the contract call function for call contarct function.
    • args the params of contract. The args content is JSON string of parameters array.

binary any binary data with a length limit = 64bytes. [optional]

Notice:

  • from = to when deploying a contract, the to address must be equal to the from address.
  • nonce the value is plus one(+1) on the nonce value of the current from address. Current nonce can be obtained from GetAccountState.
  • gasPrice and gasLimit needed for every transaction. We recommend using GetGasPrice and EstimateGas.
  • contract parameter only needed for smart contract deployment and calling. When a smart contract is deployed, the source and sourceType must be specified, the args are optional and passed when the initialization function takes a parameter. The function field is used to call the contract method.
Returns

txhash transaction hash.

contract_address returns only for deploying contract transaction.

Normal Transaction Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transaction -d '{"from":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","to":"n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17", "value":"1000000000000000000","nonce":1000,"gasPrice":"1000000","gasLimit":"2000000"}'

// Result
{
    "result":{
      "txhash":"fb5204e106168549465ea38c040df0eacaa7cbd461454621867eb5abba92b4a5",
      "contract_address":""
    }
}
Smart Contract Deployment Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/transaction -d '{"from":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","to":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5", "value":"0","nonce":2,"gasPrice":"1000000","gasLimit":"2000000","contract":{
"source":"\"use strict\";var BankVaultContract=function(){LocalContractStorage.defineMapProperty(this,\"bankVault\")};BankVaultContract.prototype={init:function(){},save:function(height){var deposit=this.bankVault.get(Blockchain.transaction.from);var value=new BigNumber(Blockchain.transaction.value);if(deposit!=null&&deposit.balance.length>0){var balance=new BigNumber(deposit.balance);value=value.plus(balance)}var content={balance:value.toString(),height:Blockchain.block.height+height};this.bankVault.put(Blockchain.transaction.from,content)},takeout:function(amount){var deposit=this.bankVault.get(Blockchain.transaction.from);if(deposit==null){return 0}if(Blockchain.block.height<deposit.height){return 0}var balance=new BigNumber(deposit.balance);var value=new BigNumber(amount);if(balance.lessThan(value)){return 0}var result=Blockchain.transfer(Blockchain.transaction.from,value);if(result>0){deposit.balance=balance.dividedBy(value).toString();this.bankVault.put(Blockchain.transaction.from,deposit)}return result}};module.exports=BankVaultContract;","sourceType":"js", "args":""}}'

// Result
{
    "result":{
        "txhash":"3a69e23903a74a3a56dfc2bfbae1ed51f69debd487e2a8dea58ae9506f572f73",
        "contract_address":"n21Y7arNbUfLGL59xgnA4ouinNxyvz773NW"
    }
}

SignHash

SignHash sign the hash of a message.

Protocol Method API
gRpc SignHash
HTTP POST /v1/admin/sign/hash
Parameters

address Sign address

hash A sha3256 hash of the message, base64 encoded.

alg Sign algorithm

  • 1 SECP256K1
Returns

data Signed transaction data.

sign normal transaction Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/sign/hash -d '{"address":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5","hash":"W+rOKNqs/tlvz02ez77yIYMCOr2EubpuNh5LvmwceI0=","alg":1}'

// Result
{
    "result":{
        "data":"a7HHsLRvKTNazD1QEogY+Fre8KmBIyK+lNa4zv0Z72puFVkY9uZD6nGixGx/6s1x6Baq7etGwlDNxVvHsoGWbAA="
    }
}

StartPprof

StartPprof starts pprof

Protocol Method API
gRpc Pprof
HTTP POST /v1/admin/pprof
Parameters

listen the address to listen

Returns

result start pprof result

Example
// Request
curl -i -H 'Content-Type: application/json' -X POST http://localhost:8685/v1/admin/pprof -d '{"listen":"0.0.0.0:1234"}'

// Result
{
    "result":{
        "result":true
    }
}

GetConfig

GetConfig return the config current neb is using

Protocol Method API
gRpc GetConfig
HTTP GET /v1/admin/getConfig
Parameters

none

Returns

config neb config

Example
// Request
curl -i -H 'Content-Type: application/json' -X GET http://localhost:8685/v1/admin/getConfig

// Result
{
    "result":{
        "config":{
            "network":{
                "seed":[],
                "listen":["0.0.0.0:8680"],
                "private_key":"conf/network/ed25519key",
                "network_id":1
            },
            "chain":{
                "chain_id":100,
                "genesis":"conf/default/genesis.conf",
                "datadir":"data.db",
                "keydir":"keydir",
                "start_mine":true,
                "coinbase":"n1QZMXSZtW7BUerroSms4axNfyBGyFGkrh5",
                "miner":"n1Zn6iyyQRhqthmCfqGBzWfip1Wx8wEvtrJ",
                "passphrase":"",
                "enable_remote_sign_server":false,
                "remote_sign_server":"",
                "gas_price":"",
                "gas_limit":"",
                "signature_ciphers":["ECC_SECP256K1"]
            },
            "rpc":{
                "rpc_listen":["127.0.0.1:8684"],
                "http_listen":["127.0.0.1:8685"],
                "http_module":["api","admin"],
                "connection_limits":0,
                "http_limits":0,
                "http_cors":[]
            },
            "stats":{
                "enable_metrics":false,
                "reporting_module":[],
                "influxdb":{
                    "host":"http://localhost:8086",
                    "port":0,
                    "db":"nebulas",
                    "user":"admin",
                    "password":"admin"
                },
                "metrics_tags":[]
            },
            "misc":null,
            "app":{
                "log_level":"debug",
                "log_file":"logs",
                "log_age":0,
                "enable_crash_report":true,
                "crash_report_url":"https://crashreport.nebulas.io",
                "pprof":{
                    "http_listen":"0.0.0.0:8888",
                    "cpuprofile":"",
                    "memprofile":""
                },
                "version":"0.7.0"
            }
        }
    }
}
REPL console

Nebulas provide an interactive javascript console, which can invoke all API and management RPC methods. The console is connected to the local node by default without specifying host.

start console

Start console using the command:

./neb console

In the case of not specifying the configuration file, the terminal‘s startup defaults to the configuration of conf/default/config.conf. If the local configuration file is not available or you want to specify the configuration file, the terminal starts like this:

./neb -c <config file> console
console interaction

The console can use the admin.setHost interface to specify the nodes that are connected. When the console is started or the host is not specified, the terminal is interacting with the local node. Therefore, you need to start a local node before starting the console.

> admin.setHost("https://testnet.nebulas.io")

Tips: The Testnet only starts the RPC interface of the API, so only the api scheme is available.

console usage

We have API and admin two schemes to access the console cmds. Users can quickly enter instructions using the TAB key.

> api.
api.call                    api.getBlockByHash          api.getNebState             api.subscribe
api.estimateGas             api.getBlockByHeight        api.getTransactionReceipt
api.gasPrice                api.getDynasty              api.latestIrreversibleBlock
api.getAccountState         api.getEventsByHash         api.sendRawTransaction
> admin.
admin.accounts                      admin.nodeInfo                      admin.signHash
admin.getConfig                     admin.sendTransaction               admin.signTransactionWithPassphrase
admin.lockAccount                   admin.sendTransactionWithPassphrase admin.startPprof
admin.newAccount                    admin.setHost                       admin.unlockAccount

Some management methods may require passphrase. The user can pass in the password when the interface is called, or the console prompts the user for input when the password is not entered. We recommend using a console prompt to enter your password because it is not visible.

Enter the password directly:

> admin.unlockAccount("n1UWZa8yuvRgePRPgp8a2jX4J9UwGXfHp6i", "passphrase")
{
    "result": {
        "result": true
    }
}

Use terminal prompt:

> admin.unlockAccount("n1UWZa8yuvRgePRPgp8a2jX4J9UwGXfHp6i")
Unlock account n1UWZa8yuvRgePRPgp8a2jX4J9UwGXfHp6i
Passphrase:
{
    "result": {
        "result": true
    }
}

The interfaces with passphrase prompt:

admin.newAccount
admin.unlockAccount
admin.signHash
admin.signTransactionWithPassphrase
admin.sendTransactionWithPassphrase

The command parameters of the command line are consistent with the parameters of the RPC interface. NEB RPC and NEB RPC_Admin.

console exit

The console can exit with the ctrl-C or exit command.

开发博客

此处是一些关于如何在星云开发的独立话题和帮助文档。

GDB调试
概览

Last week we found a lot of “Failed to update latest irreversible block.” in neb log with Leon. The reference code (nebulasio/go-nebulas/core/blockchain.go updateLatestIrreversibleBlock ) , in the code we found the cur variable is not equal to the tail variable , why? to find the cause, we try to use tool to dynamically display variable information and facilitate single-step debugging.

Goroutines

In c++ program we often use gbd to debug, so we think why not to use gdb to debug golang program . First we try to look up the BlockChain loop goroutine state and print the variables .

In c++ we all use info threads and thread x to show thread info but in the golang program ,we should use info goroutines and goroutine xx bt to displays the current list of running goroutines.

(gdb) info goroutines Undefined info command: “goroutines“. Try “help info“. (gdb) source /usr/local/go/src/runtime/runtime-gdb.py Loading Go Runtime support. (gdb) info goroutines

1 waiting  runtime.gopark
2 waiting  runtime.gopark
3 waiting  runtime.gopark
4 waiting  runtime.gopark
5 syscall  runtime.notetsleepg
6 syscall  runtime.notetsleepg
7 waiting  runtime.gopark
... ...

(gdb) goroutine 84 bt

 #0 runtime.gopark (unlockf={void (struct runtime.g , void , bool *)} 0xc420c57c80, lock=0x0, reason="select", traceEv=24 '\030', traceskip=1) at /data/packages/go/src/runtime/proc.go:288
 #1 0x0000000000440fd9 in runtime.selectgo (sel=0xc420c57f48, ~r1=842353656960) at /data/packages/go/src/runtime/select.go:395
 #2 0x0000000000ad2d73 in github.com/nebulasio/go-nebulas/core.(*BlockChain).loop (bc=0xc4202c6320)at /neb/golang/src/github.com/nebulasio/go-nebulas/core/blockchain.go:184
 #3 0x0000000000460421 in runtime.goexit () at /data/packages/go/src/runtime/asm_amd64.s:2337
 #4 .....

But neb has too many goroutines, we don’t kown which one , we give up

BreakPoints

Second we try to set break point to debug

(gdb) b blockchain.go:381

Breakpoint 2 at 0xad4373: file /neb/golang/src/github.com/nebulasio/go-nebulas/core/blockchain.go, line 381.

(gdb) b core/blockchain.go:390

Breakpoint 3 at 0xad44c6: file /neb/golang/src/github.com/nebulasio/go-nebulas/core/blockchain.go, line 390.

(gdb) info breakpoints // show all breakpoints

(gdb) d 2 //delete No 2 breakpoint

Now let the neb continue its execution until the next breakpoint, enter the c command: (gdb) c Continuing

Thread 6 "neb" hit Breakpoint 2, github.com/nebulasio/go-nebulas/core.(*BlockChain).updateLatestIrreversibleBlock (bc=0xc4202c6320, tail=0xc4244198c0)
at /neb/golang/src/github.com/nebulasio/go-nebulas/core/blockchain.go:382
382        miners := make(map[string

now we can use p(print) to print variables value

(gdb) `p cur`
$2 = (struct github.com/nebulasio/go-nebulas/core.Block *) 0xc420716f90
(gdb) `p cur.height`
$3 = 0
(gdb) `p bc`
$4 = (struct github.com/nebulasio/go-nebulas/core.BlockChain *) 0xc4202c6320
(gdb) `p bc.latestIrreversibleBlock`
$5 = (struct github.com/nebulasio/go-nebulas/core.Block *) 0xc4240bbb00
(gdb) `p bc.latestIrreversibleBlock.height`
$6 = 51743
(gdb) `p tail`
$7 = (struct github.com/nebulasio/go-nebulas/core.Block *) 0xc4244198c0
(gdb) `p tail.height`
$8 = 51749

now we can use info goroutines again, to find current goroutine. info goroutines with the * indicating the current execution, so we find the current goroutine nunmber quickly.

the next breakpoint we can use c command , so we found the cur and lib is not equal, because of length of the miners is less than ConsensusSize, In the loop the cur change to the parent block .

Other

When compiling Go programs, the following points require particular attention:

  • Using -ldflags “-s“ will prevent the standard debugging information from being printed
  • Using -gcflags “-N-l“ will prevent Go from performing some of its automated optimizations -optimizations of aggregate variables, functions, etc. These optimizations can make it very difficult for GDB to do its job, so it‘s best to disable them at compile time using these flags.
neb-dont-generate-coredump-file
OverView

During Testing, neb may be crash, and we want to get the coredump file which could help us to find the reason. However, neb don‘t generate coredump file by default. We can find the crash log in /var/log/apport.log when a crash occurred:

"called for pid 10110, signal 11, core limit 0, dump mode 1 "

The coredump file is very very important, it can serve as useful debugging aids in several situations, and help us to debug quickly. Therefore we should make neb to generate coredump file.

Set the core file size

We can use ulimit -a command to show core file size. If it‘s size is zero, which means coredump file is disabled, then we should set a value for core file size. for temporarily change we can use ulimit -c unlimited , and for permanently change we can edit /etc/security/limits.conf file, it will take effect after reboot or command sysctl -p.

<domain>    <type>    <item>        <value>
 * soft    core            unlimited

But these ways are‘t work, neb still can‘t generate coredump file and cat /proc/$pid/limits always “Max core file size 0“

Why? Why? Why? It doesn‘t Work
  1. If the setting is wrong? Just try a c++ programe build, run it and we can find that it can generate coredump.
  2. Neb is started by supervisord, is it caused by supervisord?
  3. Try to start neb without supervisord, then the neb coredump is generated!
  4. Yes, the reason is supervisord, then we can google “supervisord+coredump“ to solve it.
Solution

Supervisord only set RLIMIT_NOFILE, RLIMIT_NOPROC by set_rlimits , others are seted default 0 1. modify supervisord code options.py in 1293 line

vim /usr/lib/python2.6/site-packages/supervisor/options.py

soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
resource.setrlimit(resource.RLIMIT_CORE, (-1, hard))
  1. restart supervisord and it works .
Other seetings

You can also change the name and path of coredump file by changing file /proc/sys/kernel/core_pattern:

echo "/neb/app/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

%p: pid
%: '%' is dropped
%%: output one '%'
%u: uid
%g: gid
%s: signal number
%t: UNIX time of dump
%h: hostname
%e: executable filename
%: both are dropped
崩溃报告

In this doc, we introduce the crash reporter in Nebulas, which is used to collect crash reports in Nebulas and send it back to Nebulas Team, so the whole community can help improving the quality of Nebulas.

Overview

We, the Nebulas Team and the Nebulas community, always try our best to ensure the stability of Nebulas, since people put their faith and properties on it. That means critical bugs are unacceptable, and we are aware of that. However, we can‘t blindly think Nebulas is stable enough or there won‘t be any bugs. Thus, we have plan B, the crash reporter, to collect crash report and send it back to Nebulas community. We hope the whole community can leverage the crash reports and keep improving Nebulas.

Using crash reporter is a very common practice. For example, Microsoft Windows includes a crash reporting service called Windows Error Reporting that prompts users to send crash reports to Microsoft for online analysis. The information goes to a central database run by Microsoft. Apple also involves a standard crash reporter in macOS, named Crash Reporter. The Crash Reporter can send the crash logs to Apple Inc, for their engineers to review. Opensource community also have their own crash reporter, like Bug Buddy for Gnome, Crashpad for Chrome, Talkback for Mozilla, and etc.

In Nebulas, the crash reporter just works like the other crash reporters. It‘s aware of the crash, collects necessary information about the crash, and sends it back the Nebulas server. The server is hosted by Nebulas, and accessible for the whole community.

As a opensource, decentralized platform, we are aware of that the crash reporter may violate some users‘ privacy concern. Thus, we remove all private information in the crash report, like the user name, user id, user‘s home path and IP address. Furthermore, the crash reporter is optional and users may choose close it if users still have some concerns.

How to use it

To enable or disable the crash reporter, you need to look into the configuration file, config.conf, and change enable_crash_reporter to true to enable it, while false to disable it.

How it works

In this section, we would like to share some tech details. If you are not interested in the details, you can ignore this section.

The crash reporter is actually a daemon process, which is started by neb. When starting the crash reporter, neb will tell it the process id (pid) of neb process, and the crash file path. For the crash reporter, it will periodically check if the neb process and the crash file exists. At the time it finds the crash file, it will eliminate the private information and send it back to Nebulas.

Currently, the crash report is generated by the stderr output from neb. We‘d like the work with the whole community to collect detailed information in the future.

如何debug星云项目

作者:Wenbo Liu aries.lwb@gmail.com, July 17, 2017

Go-Nebulas项目地址:https://github.com/nebulasio/go-nebulas.git

简介
这篇短文基于Mac OSX 和 Ubuntu系统,简单介绍如何调试Go-Nebulas项目,主要介绍三种方法调试:dlv命令行调试,Gogland IDE调试,以及Visual Studio Code调试。
调试器Delve安装

在 Mac OSX 上安装Delve

Google官方为golang的调试例子用gdb,但是delve是更合适的调试器,比gdb能提供更多的信息。安装delve,在Mac上一般采用Homebrew。但是很遗憾,在本文写就时,Homebrew提供的delve包老旧,有bug,无法正确调试Go-Nebulas。普通的go项目是可以的,具体体现就是调试Go-Nebulas项目时,断点无法停住,会永远hang住。我们必须从github上下载delve的最新源代码编译成delve binary,步骤如下:

先用Homebrew安装有bug的Delve:

brew install go-delve/delve/delve
rm /usr/local/bin/dlv

安装此有问题的Delve,其实就是为了让它帮我们在Mac机器上签发一个自签名的dlv-cert证书。如果你自己愿意繁琐的手动创建证书,也可以不用安装Delve,参考https://github.com/derekparker/delve/blob/v0.12.2/Documentation/installation/osx/install.md的【Create a self-signed certificate】。 第二条rm命令是为了删除这个有问题的dlv binary,我们需要从源码编译出一个正确的版本,并且利用Homebew为我们安装的证书做codesign。 下载源代码

mkdir -p /Users/xxx/go-delve/src/github.com/derekparker
cd /Users/xxx/go-delve/src/github.com/derekparker
git clone https://github.com/derekparker/delve.git

创建一个临时文件夹,从github下载代码。注意文件夹中标注红色的部分,必须完全一样,这是因为go项目的源码组织规则,否则下一步编译会出错,报警package not found。其它部分请根据自己机器环境设置。

编译

export GOPATH=/Users/xxx/go-delve
cd /Users/xxx/go-delve/src/github.com/derekparker/delve
make install

应该会出现如下提示,表明编译成功:

scripts/gencert.sh || (echo "An error occurred when generating and installing a new certicate"; exit 1)
go install -ldflags="-s" github.com/derekparker/delve/cmd/dlv
codesign -s "dlv-cert"  /Users/xxx/go-delve/bin/dlv

然后cp /Users/liuwb/go-delve/bin/dlv/usr/local/bin/,把编译好的dlv拷贝进/usr/local/bin目录,替换之前有bug的dlv debugger。输入命令dlv version,如果能正常运行,显示版本号,说明dlv已经被加入到PATH。

在 Ubuntu 上安装Delve

对于Ubuntu系统,可以直接使用下面的指令安装Delve:

go get -u github.com/derekparker/delve/cmd/dlv

下载Go-Nebulas工程代码

mkdir /Users/xxx/workspace/blockchain/src/github.com/nebulasio/
cd /Users/xxx/workspace/blockchain/src/github.com/nebulasio/
git clone https://github.com/nebulasio/go-nebulas.git

创建一个临时文件夹,从github下载代码。注意文件夹中标注红色的部分,必须完全一样,这是因为go项目的源码组织规则,其它部分请根据自己机器环境设置。

Delve命令行调试 如果你以前用gdb调试过C程序,对dlv命令行调试的风格也不会陌生。完整的dlv命令文档,参见https://github.com/derekparker/delve/blob/master/Documentation/usage/dlv.md 这里只介绍debug部分。

输入如下命令进入调试

export GOPATH=/Users/xxx/workspace/blockchain/
cd /Users/xxx/workspace/blockchain/
dlv debug github.com/nebulasio/go-nebulas/cmd/neb -- --config /Users/xxx/workspace/blockchain/src/github.com/nebulasio/go-nebulas/conf/default/config.conf

运行无误的话,会进入debug session:

Type 'help' for list of commands.
(dlv)

我们打算在neb的函数入口设置断点,输入命令

(dlv) break main.neb
Breakpoint 1 set at 0x4ba6798 for main.neb() ./src/github.com/nebulasio/go-nebulas/cmd/neb/main.go:80
(dlv)

dlv调试器提示代码将在cmd/neb/main.go的行号80行停住,注意这时neb程序还没有运行。输入命令continue:

(dlv) continue
> main.neb() ./src/github.com/nebulasio/go-nebulas/cmd/neb/main.go:80 (hits goroutine(1):1 total:1) (PC: 0x4ba6798)
    75:        sort.Sort(cli.CommandsByName(app.Commands))
    76:
    77:        app.Run(os.Args)
    78:    }
    79:
=>  80:    func neb(ctx *cli.Context) error {
    81:        n, err := makeNeb(ctx)
    82:        if err != nil {
    83:            return err
    84:        }
    85:

查看变量,可用print命令:

(dlv) print ctx
*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Context {
    App: *github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.App {
        Name: "neb",
        HelpName: "debug",
        Usage: "the go-nebulas command line interface",
        UsageText: "",
        ArgsUsage: "",
        Version: ", branch , commit ",
        Description: "",
        Commands: []github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command len: 11, cap: 18, [
            (*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command)(0xc4201f4000),
            (*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command)(0xc4201f4128),
            (*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command)(0xc4201f4250),
            (*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command)(0xc4201f4378),
            (*github.com/nebulasio/go-nebulas/vendor/github.com/urfave/cli.Command)(0xc4201f44a0),

更多技术资料,请参考 https://github.com/derekparker/delve/tree/master/Documentation/cli https://blog.gopheracademy.com/advent-2015/debugging-with-delve/ http://hustcat.github.io/getting-started-with-delve/

Visual Studio Code调试

Visual Studio Code是微软公司发布的跨平台代码编辑工具,下载地址:https://code.visualstudio.com/Download VS Code需要安装Go插件

_images/vscode.png

打开文件夹/Users/xxx/workspace/blockchain/src/github.com/nebulasio/go-nebulas/,在.vscode文件夹下创建两个文件settings.json和launch.json。 settings.json文件内容:

// Place your settings in this file to overwrite default and user settings.
{
    "go.gopath": "/Users/xxx/workspace/blockchain/",
    "go.formatOnSave": true,
    "go.gocodeAutoBuild": false,
    "go.toolsGopath": "/Users/xxx/workspace/gotools",
    "explorer.openEditors.visible": 0,
}

go.toolsGopath是analysis tools安装的地址,可以指定为任何目录,这些analysis tools可以供其它workspace共享。

launch.json文件内容:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${workspaceRoot}/cmd/neb",
            "env": {
                "GOPATH": "/Users/xxx/workspace/blockchain/"
            },
            "args": [
                "--config",
                "/Users/xxx/workspace/blockchain/src/github.com/nebulasio/go-nebulas/conf/default/config.conf"
            ],
            "showLog": true
        }
    ]
}

在cmd/neb/main.go,neb函数中设置断点,F5运行,Go-Nebulas项目会进行编译运行,停在断点: _images/delve-vscode-debug.png

然后,就可以开心的启动Nebulas代码调试之旅!
共建指南

The go-nebulas project welcomes all contributors. The process of contributing to the Go project may be different than many projects you are used to. This document is intended as a guide to help you through the contribution process. This guide assumes you have a basic understanding of Git and Go.

Becoming a contributor

Before you can contribute to the go-nebulas project you need to setup a few prerequisites.

Preparing a Development Environment for Contributing
Setting up dependent tools
1. Go dependency management tool

dep is an (not-yet) official dependency management tool for Go. go-nebulas project use it to management all dependencies.

For more information, please visit here

2. Linter for Go source code

Golint is official linter for Go source code. Every Go source file in go-nebulas must be satisfied the style guideline. The mechanically checkable items in style guideline are listed in Effective Go and the CodeReviewComments wiki page.

For more information about Golint, please visit here.

3. XUnit output for Go Test

Go2xunit could convert go test output to XUnit compatible XML output used in Jenkins/Hudson.

Making a Contribution
Discuss your design

The project welcomes submissions but please let everyone know what you‘re working on if you want to change or add to the go-nebulas project.

Before undertaking to write something new for the go-nebulas, please file an issue (or claim an existing issue). Significant changes must go through the change proposal process before they can be accepted.

This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the language and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions.

Besides that, you can have an instant discussion with core developers in developers channel of Nebulas.IO on Community.nebulas.io.

Making a change
Getting Go Source

First you need to fork and have a local copy of the source checked out from the forked repository.

You should checkout the go-nebulas source repo inside your $GOPATH. Go to $GOPATH run the following command in a terminal.

$ mkdir -p src/github.com/nebulasio
$ cd src/github.com/nebulasio
$ git clone git@github.com:{your_github_id}/go-nebulas.git
$ cd go-nebulas
Contributing to the main repo

Most Go installations project use a release branch, but new changes should only be made based on the develop branch. (They may be applied later to a release branch as part of the release process, but most contributors won‘t do this themselves.) Before making a change, make sure you start on the develop branch:

$ git checkout develop
$ git pull
Make your changes

The entire checked-out tree is editable. Make your changes as you see fit ensuring that you create appropriate tests along with your changes. Test your changes as you go.

Goimports, Golint and Govet

Every Go source file in go-nebulas must pass Goimports, Golint and Govet check. Golint check the style mistakes, we should fix all style mistakes, including comments/docs. Govet reports suspicious constructs, we should fix all issues as well.

Run following command to check your code:

$ make fmt lint vet

lint.report text file is the Golint report, vet.report text file is the Govet report.

Testing

You‘ve written test code, tested your code before sending code out for review, run all the tests for the whole tree to make sure the changes don‘t break other packages or programs:

$ make test

test.report text file or test.report.xml XML file is the testing report.

Commit your changes

The most importance of committing changes is the commit message. Git will open an editor for a commit message. The file will look like:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch foo
# Changes not staged for commit:
#    modified:   editedfile.go
#

At the beginning of this file is a blank line; replace it with a thorough description of your change. The first line of the change description is conventionally a one-line summary of the change, prefixed by the primary affected package, and is used as the subject for code review email. It should complete the sentence “This change modifies Go to _.“ The rest of the description elaborates and should provide context for the change and explain what it does. Write in complete sentences with correct punctuation, just like for your comments in Go. If there is a helpful reference, mention it here. If you‘ve fixed an issue, reference it by number with a # before it.

After editing, the template might now read:

math: improve Sin, Cos and Tan precision for very large arguments

The existing implementation has poor numerical properties for
large arguments, so use the McGillicutty algorithm to improve
accuracy above 1e10.

The algorithm is described at http://wikipedia.org/wiki/McGillicutty_Algorithm

Fixes #159

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch foo
# Changes not staged for commit:
#    modified:   editedfile.go
#

The commented section of the file lists all the modified files in your client. It is best to keep unrelated changes in different commits, so if you see a file listed that should not be included, abort the command and move that file to a different branch.

The special notation “Fixes #159“ associates the change with issue 159 in the go-nebulas issue tracker. When this change is eventually applied, the issue tracker will automatically mark the issue as fixed. (There are several such conventions, described in detail in the GitHub Issue Tracker documentation.)

Creating a Pull Request

For more information about creating a pull request, please refer to the Create a Pull Request in Github page.

下载

Bleeding edge code can be cloned from the branch of their git repositories:

主网

星云主网 鹰星云(Eagle Nebula)于2018年3月30日发布。实现公链基本功能,有2大特点:

  • 支持Javascript编写智能合约
  • 交易处理能力达到2000TPS

星云新星(Nebulas NOVA)于2018年底上线,有3大特点:

  • 星云指数:衡量区块链世界链上数据价值
  • 星云区块链可执行环境:使区块链核心协议可即时升级
  • 开发者激励协议:给予区块链应用开发者链上原生激励

点击这里了解星云NOVA 。相关文章:

第三个重要版本关于贡献度证明机制,将于2020年随节点计划发布。 点击这里了解星云节点计划

点击这里加入星云主网

测试网

功能完备的 星云测试网 已经上线,方便开发者随时免费使用星云。了解: 加入星云测试网

星云节点计划

——基于贡献度证明机制(PoD)

V1.0.2 星云基金会,中文PDF


星云节点计划是星云 愿景 的实践:让每个人从去中心化协作中公平获益。推动 自治元网络(Autonomous Metanet) [1] 落地。

该计划核心为 贡献度证明机制(Proof of Devotion,以下简称PoD) [2],可以简单地概述为以社区贡献者贡献大小为基础构建的机制,包括共识机制和治理机制两部分。通过节点选取组建共识委员会,实现主网节点去中心化;并由优质节点组成的治理委员会以代议制的形式组织社区治理。

星云将构建全新的面向复杂数据网络的 去中心化自治组织(Decentralized Autonomous Organization,DAO) [3]

了解更多节点计划和PoD机制:

1. PoD概述

贡献度证明机制(PoD)概述

1.1 设计目标

要建设可持续发展的良性公链⽣态,需要同时兼顾共识机制的快速和不可逆性,以及治理的公平性。

当前我们面对新的应用场景,信息交互从单一、简单的功能向复杂、多样发展,用户角色也越来越多。协作场景已经从人与人面对面合作,升级到全球化的跨越多地区、并有多组织参与的协 作。协作的目标结果也更为多变,成果从实物到虚拟,协作的时间跨度也变得更长、更灵活。[1]

要保证新应用场景的治理公平性,需要采用新的协作方式。传统的中心化治理无法应对复杂的新场景。面对复杂的数据交互形态和丰富的用户角色,中心化的单一评判标准很难做到广而全面,有相当的局限性。

而传统的去中心化协作方式因为没有考虑到更多角色的存在引起的新的利益分配情况,或多或少存在利益分配不均、无法长期良性发展或者发展缓慢的情况。

如何保障社区成员的利益,让生态沉淀出价值深度,是星云在思考和实践的方向。在优先保证效率和不可逆的前提下,我们设计PoD就是为了从贡献度角度出发,尽可能追求公平性,维护社区用户的利益。

1.2 构成

PoD可以简单地概述为以社区贡献者贡献大小为基础构建的机制,该机制包括共识机制和治理机制两部分。参见图1.1。

图1.1 PoD构成

图1.1 PoD构成

PoD的人员组成涉及两个执行委员会:共识委员会和治理委员会。

  • 共识委员会:共识机制由共识委员会执行。共识委员会通过综合排名算法从所有节点中选出。
  • 治理委员会:治理机制由治理委员会落实而治理委员会由共识委员会中贡献最大的共识节点组成。

图1.2 PoD执行委员会

图1.2 PoD执行委员会

1.3 激励分配

自2018年3月31日星云主网上线以来,临时共识机制DPoS[2]每日全网产生8,219.1744 NAS记账收入,每年全网产生2,999,941 NAS。这部分记账收入将全部用于星云节点计划。其中,共识机制和治理机制两部分的激励比例约为5:1。即:

  1. 每年全网共识机制激励总量:2,499,951 NAS
  2. 每年全网治理机制激励总量:499,999 NAS

共识机制激励由共识委员会轮询周期内出过块的共识节点均分。没有入选共识节点的候选节点在该周期内没有激励。

治理机制激励由治理委员会治理周期内参与过所有投票的治理节点均分。没有参与或只参与过部分投票的治理节点在该周期内没有激励。

1.4 载体:NAX

贡献度证明的载体是NAX[3]。NAX仅可以通过去中心化质押(dStaking)[4]的方式,质押NAS而得。根据 《星云链NAX白皮书》Github, PDF)所述,NAX采用动态分发策略,实际总发行量与全局质押率相关,某地址获得NAX的数量与NAS质押量以及币龄相关,可以看作是衡量了该地址对社区作出的持币贡献。因此,NAX可以被看作为星云生态的贡献有效凭证。

在星云社区协作平台Go Nebulas上,也将以NAX作为生态贡献激励,鼓励社区成员建设社区。


[1] Orange Paper: Nebulas Governance 星云治理橙皮书

[2] Delegated Proof of Stake Consensus(DPoS): 由持币的利益相关方投票来选择代表,代表们再以民主投票的方式决定共识问题。包括但不限于:所有网络参数、费用估算、块间隔、交易规模等。

[3] NAX: 通过去中心化质押产生。nextDAO上的首个Token。用户通过质押NAS获得NAX。NAX采用动态分发策略,实际总发行量与全局质押率相关,用户个人获得NAX的数量与NAS质押量以及币龄相关。

[4] dStaking 去中心化质押: 区别于需要向合约转账的传统质押,去中心化质押的合约记录着用户的质押契约,用户资产仍然存放在用户个人地址之上。

2. 共识机制

本章节主要介绍PoD机制的共识机制部分,具体如下:

共识机制使用智能合约自动管理,主要由节点选取规则和共识算法两部分组成,联合完成出块,保障主网正常运行。

星云链平均出块时间为15秒。设定每个轮询周期内,21个共识节点轮番出块,每个节点出块10个。故一个轮询周期默认为210个区块高度,约为52.5分钟。每个轮询周期内的共识机制执行流程如下图所示:

图2.1 每个轮询周期的共识机制执行流程

图2.1 每个轮询周期的共识机制执行流程

2.1 节点入选基础条件

任何个人或组织均可申请成为节点,必须满足以下所有要求才有资格参与候选节点选取:

  • 服务器满足基本要求(参见附录A 推荐硬件配置);
  • 服务器保证运行中;
  • 质押(即投票)不小于10万NAX;
  • 保证金质押2万NAS;
  • 没有高安全等级作恶记录(参见2.5.1 惩罚
2.2 节点选取规则

节点选取规则包括两个步骤:

  1. 候选节点选取: 在每个轮询周期内,在满足基本入选要求的所有节点中,按候选节点综合排名算法,选出51个候选节点;
  2. 共识节点选取: 在每个轮询周期内,按共识节点选取算法,在稳定且最能代表用户权益的一组候选节点中选出21个共识节点。共识节点负责出块,可以获得共识机制激励。

候选节点和共识节点共同构成共识委员会。选取流程如下图所示:

图2.2 节点选取流程

图2.2 节点选取流程

2.2.1 候选节点综合排名算法

在满足申请节点基本要求(2.1 节点入选基础条件)的前提下,将所有节点按候选节点综合排名算法进行排名,靠前的51个节点为候选节点。候选节点综合排名算法参考两个因素:NAX投票数V(i)和出块稳定指数S(i)。候选节点综合排名指数R(i)为:

R(i) = V(i) × S(i)

假设S(i)相同,NAX投票数V(i)也相同,则最先达到NAX投票数V(i)的节点入选。

2.2.1.1 NAX投票数

NAX投票数V(i):所有社区成员和节点负责人都可以向支持的节点投出NAX,助力该节点提升排名。

2.2.1.2 出块稳定指数

出块稳定指数S(i):由共识节点成功出块的比率决定。未出过块的节点初始值S为0.8。在每个轮询周期内,一个候选节点有未参与出块、成功出块、漏出块三种情况。

A 未参与出块

未参与出块的候选节点下一周期的S(i+1)为:

S(i+1) = S(i) + 0.01

B 成功出块

共识节点在一个轮询周期内需要出块10个。如果10个块全部出了则下一周期的S(i+1)为:

S(i+1) = S(i) + 0.1 (S <= 1)

S(i)逐步提升到1。一般情况下,稳定出块的节点S(i) = 1。

C 漏出块

如果该共识节点发生漏出块的情况,则下一周期的S(i+1)为:

S(i+1) = S(i) × (10 - C) / 10

其中,C为漏出块个数。C越大,S(i)值约低。如果S(i)降至阈值K(K初始值为0.5),则该共识节点在未来20个轮询周期中都不能入选候选节点,详见2.5.1 惩罚

2.2.2 共识节点选取算法

共识节点从51个候选节点中选出。从候选节点中选取共识节点的具体方式如下:

  1. 排名前6的候选节点直接入选。
  2. 14个共识节点的名额按如下公式从排名第7到第51名的候选节点中选取:
RConsensus = (R(i) / Sum(R)) × Random()

各参数说明如下:

RConsensus: 共识节点排名指数。

R(i) 候选节点综合排名指数。R(i)已经综合了两个因素:NAX投票数V(i)和出块稳定指数S(i),故可以认为R(i)同时考虑了节点的社区支持度和历史出块贡献。

Sum(R): 51个候选节点的综合排名指数总和。R(i)/Sum(R)可以看作是该节点的贡献度在所有51个候选节点中的占比。

Random(): 随机概率。

3.最后1个共识节点在排名第7到第51名且没被选上的候选节点中随机选取,称为幸运节点。

2.3 共识算法

共识算法基于DPoS共识机制,通过共识委员会共识节点调度生产下一个轮询周期的区块。21个共识节点轮流出块。在一个轮询周期结束后,切换到下一个轮询周期的共识节点,轮流出块。

区块确认使用BFT[1]共识保证区块上链的一致性,以实现PoD的稳定性、公平性和安全性。

2.3.1 出块顺序

一个轮询周期内的出块顺序由21个共识节点经过VRF[2]随机排序。在每个轮询周期内,负责出块的共识节点和顺序始终保持不变。

2.3.2 打包出块

共识节点在出块时会打包交易缓存池中的交易。具体方式如下:

  1. 共识节点严格按照该轮询周期内的出块顺序和时间打包出块;
  2. 在打包时间内尽可能多的打包未上链交易;
  3. 优先打包GasPrice高的交易;
  4. 不可上链的交易在执行失败后会被丢掉。
2.3.3 区块上链确认

共识节点的区块上链确认保证了链的一致性和安全性,并对作恶节点进行惩罚。区块上链遵循如下规则:

  1. 优先选取最长子链作为最优链;
  2. 在长度一致的子链中按哈希排序选择最优链;
  3. 最后不可逆区块使用BFT共识需要⅔+1的共识节点确认;
  4. 对非出块时间出块,双花等攻击采取惩罚机制(参见2.5.1 惩罚)。
2.4 退出机制

PoD的投票是公平的自由博弈场景。所有社区成员都可以随时申请撤销NAX投票或退出节点选取。

2.4.1 撤销投票

所有社区成员或组织,可以随时发起撤销投票的申请。当撤销投票申请成功提出后,该节点的NAX投票数(2.2.1.1 NAX投票数,V(i))即刻下调,即时影响下一轮询周期该节点排名。按照2.1 节点入选基础条件,如果NAX投票数低于10万NAX,该节点无法入选候选节点。

所撤销投票的额度: 可以任意设置。但所有社区成员或组织只能申请撤销自己投出的NAX。

NAX返还时间: 撤销投票申请成功提交后120个轮询周期(约为5天)后返还NAX至原地址。

2.4.2 节点退出

所有节点都可以随时退出。一旦选择退出,该节点将即刻失去成为下一周期候选节点的资格。

保证金返还额度: 一次性全额返还,不可选择额度。

保证金返还时间: 节点退出申请成功提交后820个轮询周期(约为1个月)后返还NAS至原地址。

NAX返还时间: 节点退出申请成功提交后120个轮询周期(约为5天)后返还所有参与者的NAX至原地址。

2.5 惩罚和应急响应
2.5.1 惩罚
2.5.1.1 出块惩罚

为了维护PoD系统的安全性,根据情节严重情况进行相应的惩罚,增加节点作恶成本。三种针对共识节点的出块惩罚如下:

安全等级 危害 举例 惩罚
选取限制 惩罚金
造成系统不流畅 发生漏出块等间断性不参与共识的情况 出块稳定指数S(i)2.2.1.2)下降,当降至0.5以下则20个轮询周期内(约为1天)不得入选候选节点 不冻结
造成系统不稳定 一个周期内一个块也没出 20个轮询周期内(约为1天)不得入选候选节点 冻结5%保证金
威胁系统安全 同一高度出多个块或在不应出块的时候出块、女巫攻击等 永久冻结 冻结全额保证金和该节点所有NAX(含用户所投)

表2.1:共识机制安全等级表

中、高安全等级惩罚处理流程:

  1. 发生安全问题时,选取限制自动执行。并在200个轮询周期内(约为7天)内观察该节点是否恢复出块至少一次。
    1. 如果该节点恢复出块至少一次,则惩罚取消。
    2. 如果依然没有出块,视为没有解决问题,会冻结保证金。
    3. 如果在出现漏出块情况后,节点选择主动退出,则按照规则,在撤销投票申请成功提交后120个轮询周期(约为5天)后返还NAX至原地址。
  2. 在最近一个治理周期投票阶段,由治理委员会投票判定该节点惩罚是否成立。
    1. 如果投票通过该共识节点危害PoD系统安全性的行为属实,则对该节点的惩罚有效,选取限制继续执行,冻结中的惩罚金捐赠给Go.nebulas社区协作基金(参见3.2.2 社区资产)。
    2. 如果投票不通过,该节点未对系统安全性造成危害,则选取限制终止,出块稳定指数S(i)恢复为惩罚前的数值,已被冻结的惩罚金解冻。

具体参见3.2 治理范围的3.2.3 共识机制惩罚3.3.3 投票结果处理

2.5.1.2 治理惩罚

除了出块惩罚,当出块节点当选治理节点后,如果在连续2个治理周期内没有完成治理任务,则820个轮询周期内(约一个月)不能入选候选节点。详情参见3.4.1 单个治理节点惩罚

2.5.2 应急响应

如果发生黑客攻击主网等突发事件,为了保证主网可以快速相应,降低危害,星云基金会预留应急合约管理方法,星云基金会可以即刻将有问题的地址加入黑名单,禁止该地址转账。

整个流程公开透明。紧急处理后星云基金会应回溯事件过程和处理方式,接受社区监督。


[1] BFT(Byzantine Fault Tolerance)即拜占庭容错: 是分布式计算领域的容错技术,拜占庭容错来源于拜占庭将军问题。拜占庭将军问题是对现实世界的模型化,由于硬件错误、网络拥塞或中断以及遭到恶意等原因,计算机和网络可能出现不可预料的行为。拜占庭容错技术被设计用来处理现实存在的异常行为,并满足所要解决的问题的规范要求。

[2] VRF(Verifiable Random Function)可验证随机函数: 是一种将输入映射为可验证的伪随机输出的加密方案。该方案是由Micali(Algorand的创建者)、Rabin以及Vadhan于1999年提出的。至今VRF已广泛应用于各种加密场景、协议和系统中。

3. 治理机制

本章节主要介绍PoD机制的治理机制部分,具体如下:

星云链关注不同角色在去中心化协作中对生态作出的贡献,治理机制是PoD的重要组成部分。治理机制是由治理委员会负责,以链上投票为主要手段,对社区协作和社区资产进行管理,从而实现社区自治的一系列机制。

3.1 治理委员会

治理机制的执行由治理委员会负责管理。治理委员会由治理节点组成。

治理周期: 每820个轮询周期(约1个月)为一个治理周期。

治理节点选取: 治理节点从共识委员会中选出,在一个治理周期内(820个轮询周期),出块数最多的51个共识节点有资格成为下一治理周期治理节点。如果出块数一样多,则先达到该出块数的节点排名靠前。

3.2 治理范围
3.2.1 社区协作

星云社区的提案机制是星云自治元网络的重要组成部分。当前星云社区的提案和项目都通过星云协作平台Go.nebulas(go.nebulas.io)展示和管理,所有社区成员都可以在Go.nebulas上提出自己对星云未来发展的意见和建议,提案包括但不限于[1]

  1. 星云主网研发
  2. 社区协作流程优化、治理建议等
  3. 现有星云社区产品的升级迭代、Bug提报
  4. 社区生态产品的开发和维护
  5. 社区运营和市场拓展

一个建议从提出到落地执行,会经过三个阶段:

  1. 提案立项期
  2. 项目执行期
  3. 验收期

每一步都需要通过治理委员会投票决议。治理委员会在每个治理周期内有2类投票任务:

  1. 提案立项投票:对该周期内星云社区的提案进行投票,决定是否可以执行并批准预算。
  2. 项目验收投票:对该周期内提交测试的项目进行投票,决定项目是否可以验收结算。

治理委员会治理流程如下图:

图3.1 治理委员会投票治理流程

图3.1 治理委员会投票治理流程

提案立项期: 社区成员在星云协作平台Go.nebulas(go.nebulas.io)上自由发起和分享提案。技术委员会推动提案立项。按照执行情况,提案分为两类:

  1. 无需申请预算的提案。 比如提案内容为星云发展方向探讨、治理组织结构调整建议、主网参数调整等。通过提案投票后,可以由相关负责人快速推进执行。
  2. 需要申请预算的提案。 此过程由星云技术委员会协助沟通,并对预算进行把关。提案人可以按照Go.nebulas的标准模版提出预算、项目目标、执行步骤和工期,申请自己执行或在社区悬赏执行人。

执行和验收期: 执行和验收期为Go.nebulas内部运作流程,治理委员会无需亲自参与。主要分为三个阶段:

  1. 悬赏期:由提案人或Go.nebulas运营团队担任项目发起人,发起项目悬赏,社区成员可自由申领;
  2. 执行期:项目发起人确认项目负责人,项目负责人开始执行并提交项目结果;
  3. 测试期:项目发起人Go.nebulas运营团队组织验收,并出具是否通过该项目的建议报告,供治理委员会参考。
3.2.2 社区资产

治理委员会负责管理社区公共资产的使用。社区公共资产包括:

  1. Go.nebulas社区协作基金的使用和分发

    此部分资金的初始来源是自星云2018年3月30日主网上线至节点去中心化之前,已经产生的DPoS记账收益。其中,除去已使用的部分(如星云激励计划奖励),剩余部分将在节点去中心化之后归入Go.nebulas社区协作基金。

    由于当前每个治理周期的项目总金额上限为不大于30,000 USDT,故启动治理机制后半年内的实际使用为180,000 USDT等额NAS。

  2. 星云节点计划激励分配

    星云节点计划激励包括共识机制激励和治理机制激励两部分。来源为DPoS每日产生的8,219.1744 NAS记账收入。具体分配方法参见1.3 激励分配

公共资产的使用、分配方案改动等,都需要经过提案流程,由治理委员会投票决议通过后方可执行。

3.2.3 共识机制惩罚

治理委员会同时需要对该治理周期内发生的中、高安全等级惩罚(参见2.5.1 惩罚)情况进行投票决议。

  1. 如果投票通过该共识节点危害PoD系统安全性的行为属实,则对该节点的惩罚有效,选取限制继续执行,已冻结的惩罚金捐赠给Go.nebulas社区协作基金。
  2. 如果投票不通过,该节点未对系统安全性造成危害,则选取限制终止,出块稳定指数S(i)恢复为惩罚前的数值,已被冻结的惩罚金解冻。
3.3 治理手段:投票
3.3.1 投票周期

治理节点可以在前一个治理周期结束后120个轮询周期内(约为5天)进行投票。逾期视为自动放弃。

3.3.2 投票方法

治理节点投票为公开非匿名链上投票。治理节点应参与该治理周期内的所有投票。每个投票有三种选项(必选一个):

  • 支持
  • 反对
  • 弃权

每个提案只能投一次,每次投出1 NAX。投票使用的NAX直接销毁,不返还。

3.3.3 投票结果处理

一个提案或项目的通过在不同投票阶段需要满足以下不同条件:

投票阶段 治理节点参与度 通过率 预算限制
提案投票 不少于26个 67%以上 *** 单个项目预算不大于15,000 USDT *

该治理周期内通过的项目总金额不大于30,000 USDT **

项目验收投票 不少于26个 67%以上 /
共识机制惩罚 不少于26个 67%以上 /

表3.1:投票结果处理表

* 单个项目预算如果超出,建议拆分为多期项目进行。

** 如果该治理周期内通过的项目总金额超预算,则按支持率排名,排名靠后的提案顺延到下一治理周期立项。

*** 通过率计算方法为:支持票 / (支持票 + 反对票)。

3.4 惩罚机制
3.4.1 单个治理节点惩罚

如果一个节点连续2个治理周期成为治理节点却没有完成全部治理投票,则820个轮询周期内(约一个月)不能入选候选节点。

3.4.2 治理失效
  1. 如果某治理周期内参与投票的治理节点少于26个,本次治理宣告失效,则治理机制激励捐赠给Go.nebulas社区协作基金(参见3.2.2 社区资产)。
  2. 如果某治理周期内一个提案和项目都没有,即没有可进行的投票,则本次治理宣告失效。治理机制激励捐赠给Go.nebulas社区协作基金。

[1] Go Nebulas帮助文档

附录
附录A 推荐硬件配置和环境

每月推荐配置服务器支出约为:150 USDT/月

  • CPU:>=4核(推荐8核)
  • RAM:>=16G
  • Disk: >= 600G SSD
  • NTP: 安装NTP服务确保服务器时间同步

节点安装教程参见星云技术文档:Nebulas 101 - 01 编译安装

推荐使用docker安装节点:

sudo docker-compose build

sudo docker-compose up -d
附录B 节点共建

节点可以由个人、公司、组织等运营。节点利益分配由节点运营方决定。

支持一个节点是社区用户的自主行为,用户应在充分考察节点运行情况后作出决定。PoD仅能保证质押NAX的正常质押和提取,不对节点运营者的承诺负责。

为了方便社区用户参加,初始阶段星云基金会将组建示范性共建节点。该节点服务器由星云基金会代为运维。所有社区用户都可以通过投出NAX来支持该节点,所获利益扣除服务器运维基本费用后,由参与共建的所有社区用户按投出的NAX额度均分。

附录C 收益模拟

假设一个节点在一个月内每日都在候选节点之列,则当月最多获得共识机制激励约为9,920 NAS。

因一个月内轮询周期超过700次,该节点当月一定可以入选共识节点。但考虑到共识节点综合排名算法有随机数因素的存在,平均收益预计在3,307 NAS左右。

假设一年内每月入选的51个治理节点都参与了治理,则每个治理节点每月可获得治理机制激励预计为816 NAS。

附录D 参数表
D.1 基本参数

平均出块时间:15秒

轮询周期:210个区块高度,约52.5分钟

治理周期:820个轮询周期(约1个月)

共识节点:21个

候选节点:51个(含21个共识节点)

治理节点:51个(每个治理周期内出块最多的共识节点)

保证金:2万NAS

候选节点最小质押量(投票量):10万NAX

保证金返还时间:节点退出申请成功提交后820个轮询周期(约为1个月)

NAX返还时间:撤销投票或节点退出申请成功提交后120个轮询周期(约为5天)

D.2 共识机制相关参数

节点应出块数:10个(每个轮询周期内)

出块稳定指数S(i)初始值:0.8

出块稳定指数S(i)最大值:1

出块稳定指数触发惩罚机制的临界值:0.5

惩罚金(中级):5%保证金

惩罚金(高级):全额保证金 + 该节点上质押的所有NAX

共识机制惩罚时长(安全级别低级和中级):20个轮询周期(约1天)内不得入选候选节点

共识机制惩罚时长(高级):永久

D.3 治理机制参数

治理节点投票时长:120个轮询周期内(约为5天)

治理节点最小参与度:26个

提案投票通过率:50%以上

项目立项投票、项目验收投票和共识机制惩罚投票通过率:67%以上

触发治理惩罚机制的次数:连续2次治理周期未参与全部治理投票

治理机制惩罚时长:820个轮询周期(约1个月)

D.4 激励分配参数

每日全网记账收入:8,219.1744 NAS

每年全网记账收入:2,999,941 NAS

每年全网共识机制激励总量:2,499,951 NAS

每年全网治理机制激励总量:499,999 NAS

单项目预算:不大于15,000 USDT

项目总金额上限:不大于30,000 USDT(一个治理周期内)

附录E 地址管理
  • 注册地址: 该地址将是你登录节点管理的唯一标识,请使用Chrome插件登录本平台管理节点。
  • 出块地址: 仅用于出块,签名,轮询检查。放在你的服务器上。
  • 激励地址: 你的共识激励将打入该地址,为了安全,推荐使用冷钱包。激励地址通过服务器配置修改。
  • 治理地址: 如果当选治理节点,这是你的治理投票地址。同时,你的治理激励将打入该地址。为了便于使用,推荐使用热钱包地址,如NAS nano pro钱包地址。

出块地址、治理地址均默认与注册地址相同。为了安全起见,建议使用不同地址,并按照我们的推荐来分配冷热钱包。

附录F 改动记录
  • 2019.11.20 - v1.0
  • 2020.06.02 - v1.0.1 PoD惩罚规则调整。NP289
  • 2020.06.26 - v1.0.2 调整出块稳定指数初始上限(NP294)、增加幸运节点(NP296)、PoD治理投票通过率统计不计入弃权项(NP295)

相关报道:The launch of Nebulas’ Proof of Devotion consensus protocol has begun on Ambcrypto.

如何加入

包含PoD的新版主网Nebulas Voyager(航行者)将于3月30日发布。

为了更好地完成主网节点去中心化,届时星云节点计划将开放节点申请。诚邀当前星云生态中活跃的项目方、合作伙伴和社区成员,搭建节点并体验治理流程。

Nebulas Community Collaboration Platform: go.nebulas.io


[1]Autonomous Metanet 自治元网络:一种基于区块链技术构建的,面向复杂数据和交互的开放协作体系。
[2]Proof of Devotion(PoD)贡献度证明机制:可以简单地概述为以社区贡献者贡献大小为基础构建的机制,包括共识机制和治理机制两部分。通过社区贡献者组建共识委员会,实现主网节点去中心化;并通过治理委员会代议制的形式参与社区治理。
[3]Decentralized Autonomous Organization(DAO)去中心化自治组织:也叫分布式自治组织。是一个以公开透明的计算机代码来体现的组织。一个分布式自治组织的金融交易记录和程序规则是保存在区块链中的。

如何做贡献

Nebulas aims for a continuously improving ecosystem, which means we need help from the community. We need your contributions! It is not limited exclusively to programming, but also bug reports and translations, spreading the tenets of Nebulas, answering questions, and so on.

1. 社区协作平台:Go.nebulas.io

Most of our projects and their corresponding bounties can be found on Nebulas Community Collabration Platform: Go.nebulas.

2. 代码
2.1 主网开发

星云的主网开发是星云技术开发中最重要的一部分内容,我们希望有更多社区的用户和开发者参与到主网开发中来。主网开发相关bounty逐步开放中,敬请期待。

Besides programming, mainnet development is still ongoing and needs the help of the community to tackle challenging problems in the blockchain industry. For instance, we need to design manipulation-resistant mechanisms for blockchain, formally verify the new consensus algorithm, improve security of the Nebulas mainnet, apply new crypto algorithms to Nebulas, etcetera.

We are excited to devote ourselves to blockchain and to see how blockchain technology can improve people‘s lives. We want to share this exciting experience with the whole community. Thus, we call upon all developers.

We are very glad that you are considering to help Nebulas Team or go-nebulas project, including but not limited to source code, documents or others. Read our guideline to learn more about development & contribution details.

关注github:https://github.com/nebulasio/go-nebulas

关注Roadmap: https://nebulas.io/cn/roadmap.html

2.2 报Bug

随时欢迎有价值的Bug!

如果发现Bug,请通过 Bug提报表格 提交。你将有机会得到奖励。具体参见星云悬赏计划

如果你发现了bug,请将其提交给星云链团队。包括但不限于:主网和测试网、nebPay、neb.js、web钱包,以及其它工具和文档。我们会遵照OWASP风险评估体系,综合bug的危险程度和覆盖面等给予提交者相应的奖励。

如果你有bug修复建议或者其它改进方案,欢迎告知,或者直接参与开发。成为链上资产安全的守护人,让星云更健壮。

主网新功能都会先上线测试网进行公测,欢迎主动参与测试,提前发现问题。同样可以获得相应奖励。

3. 文档
3.1 翻译

Translating is important to spread Nebulas to the whole world.

我们欢迎来自世界各地的社区成员,参与到星云文档的翻译之中。整个wiki的所有内容都可以翻译,包括但不限于:主网技术开发文档、DApp开发帮助手册、星云白皮书和黄皮书等官方报告、星云理念介绍等等。你的翻译将为你当地的星云开发者和用户提供方便。

注意:部分文档需要有数学、计算机科学、密码学等领域专业背景。

wiki.nebulas.io (Github) is the platform to collect all these important documents both non-technical and technical.

如何编辑页面

编辑方法请点击 这里.

Editing Software

For users who are familiar with git and would like to edit the Wiki locally, reST should be used to edit .rst files, and Pandoc Markdown for .md files.

Click here to learn about the differences between Pandoc Markdown and reST.

Below are some of the learning resources that can be used to further your knowledge of Markdown:

The aftermath

When you edit pages on Github, you should always click on “Preview changes“ to view the result of your labor. After your contribution has been merged, you can check the building process here.

3.2 写作

星云社区的开发者们需要文档帮助他们了解和使用星云链的各种功能,欢迎共同编写各类技术介绍文档和开发帮助文档。

同时,星云社区的用户也需要简洁易懂的星云介绍文档和各类工具使用手册。整个wiki都是你施展身手的舞台。

你的贡献将会被所有星云社区的开发者和用户知道,并有机会被翻译为多种语言,让更多人受益。

用户群组

用户群组是社区成员交流想法和建议的地方。当前星云有多个交流平台,参见官网社区板块:https://nebulas.io/community.html

论坛讨论:community.nebulas.io (综合)

Reddit: Reddit/r/nebulas (综合), Reddit/r/nasdev (开发者)

Telegram: English (for non-developers)

为方便星云社区开发者之间进行沟通,我们欢迎社区的开发者创建一个IRC。

_images/nebulas_square_logo128.png _images/github_square_logo128.png _images/reddit_square_logo128.png _images/twitter_square_logo128.png _images/telegram_square_logo128.png

5. 捐赠

欢迎为星云社区的发展和繁荣做出捐赠,支持NAS或ETH。同时欢迎加入物资捐助备用名单,比如Meetup场地提供、本地沟通和摄影等。会根据你的意愿,公开捐赠名单。有意愿者可发邮件至contact@nebulas.io了解详情。

中文编辑指南

https://blog.nebulas.io/wp-content/uploads/2018/12/medium-960x540.jpg

关于:

星云Wiki是星云社区协作工具,星云社区成员以协作方式发布各种文档,包括技术文档、相关资料、社区动态等。每个社区成员都可根据自己兴趣参与到星云Wiki的维护中去,并受益于日益完善的星云Wiki。

如何参与维护:

首先你得有个GitHub账号。注册流程非常简单,1分钟内即可搞定。 https://blog.nebulas.io/wp-content/uploads/2018/12/%E6%9B%BF%E6%8D%A21-1200x642.jpg https://blog.nebulas.io/wp-content/uploads/2018/12/P6-1200x579.jpg

登录Github账号,打开星云Wiki页面就能参与编辑维护了。 https://blog.nebulas.io/wp-content/uploads/2018/12/P4-1200x586.jpg 星云Wiki中文界面,点击左下角“v.latest”,在弹出的界面可切换语言版本,下载不同格式的星云Wiki文档,非常方便。

下面进入实战状态:比如我要添加一条动态。打开星云动态菜单,进入动态页面。 https://blog.nebulas.io/wp-content/uploads/2018/12/P7-1200x589.jpg

点击右上角”Edit on GitHub”,进入GitHub界面。看到图中红圈内的小笔了吗,点击后正式进入文本编辑界面。 https://blog.nebulas.io/wp-content/uploads/2018/12/p8-1200x574.jpg

咦,怎么看起来跟我们平常的Word编辑界面不一样。 没错,星云Wiki的编辑需要使用Markdown语言,跟我们平常使用的Word不那么一样。 https://blog.nebulas.io/wp-content/uploads/2018/12/P9-1200x566.jpg

下面进入学习时间:编辑星云Wiki需要使用的Markdown种类很少,主要有以下几种。 一张图看懂Markdown主要用法。 https://blog.nebulas.io/wp-content/uploads/2018/12/%E6%9C%AA%E6%A0%87%E9%A2%98-1.png

如果嫌麻烦,网上有很多好用的在线Markdowan编辑器,非常好用。 这里推荐一款:

https://www.mdeditor.com/

好了,接下来我们开始编辑内容:添加一个链接。 https://blog.nebulas.io/wp-content/uploads/2018/12/P10-1200x535.jpg

编辑完毕,可以点击“Preview changes”预览效果。如果效果令你满意,进入下一步。 https://blog.nebulas.io/wp-content/uploads/2018/12/P11-1200x483.jpg

在页面底部,简要表述更新的内容(方便管理员审核),然后点击“Propose file change”。 https://blog.nebulas.io/wp-content/uploads/2018/12/p13-1200x466.jpg

嗯,还没完。还需要点击“create pull request”,提交推送请求。 https://blog.nebulas.io/wp-content/uploads/2018/12/p15-1200x551.jpg

最后再确认一下,再次点击“create pull request”。 https://blog.nebulas.io/wp-content/uploads/2018/12/P17-1200x723.jpg

看到下面这个界面,才算大功告成。这个时候,你需要做的就是等待管理员审核。 https://blog.nebulas.io/wp-content/uploads/2018/12/P18-1200x507.jpg

管理员审核过后,我们发现更新的内容,已经可以在星云Wiki上正常显示了。 恭喜你,你已经为星云Wiki作出了贡献,任何使用星云Wiki的人,将会看到你更新的内容,并受益于你。 https://blog.nebulas.io/wp-content/uploads/2018/12/p20-1200x467.jpg

创建新页面

更进一步,如果有社区小伙伴想创建新页面,那么具体该怎么操作呢?

其实两个字:简单。 比如有社区小伙伴想在“社区”菜单下面建立“活动”一页。 那么点击右上角“Edit on GitHub”,进入GitHub编辑页面。 https://blog.nebulas.io/wp-content/uploads/2018/12/P21-1200x521.jpg

选取要建立新页面所在的文件夹,点击进入。 https://blog.nebulas.io/wp-content/uploads/2018/12/P22-1200x582.jpg

这里需要稍微解释下,md后缀的文件,代表单页页面; README.rst,用来控制文件的排列顺序。

点击右上角的“Create new file”,创建新文件。 https://blog.nebulas.io/wp-content/uploads/2018/12/P23-1200x457.jpg

上面红框内容是给后台文件命名,下面红框内容是Wiki显示标题。 https://blog.nebulas.io/wp-content/uploads/2018/12/P24-1200x483.jpg

下面的流程是不是很熟悉,跟修改页面的流程一样。这里就不展开讲了。 https://blog.nebulas.io/wp-content/uploads/2018/12/P25-1200x453.jpg

这个只是完成了第一步,接下来我们要修改下文件名称添加到目录中去,并在此提交修改。 修改通过后,新页面就可以在Wiki页面正常显示了。 https://blog.nebulas.io/wp-content/uploads/2018/12/P29-1200x472.jpg

在添加目录的时候, 需要注意两点:

README.rst中文件名称要跟之前创建的文件名称保持一致, README.rst目录的排序决定着文件在星云Wiki最终的显示顺序。

好了,我们的简明星云Wiki教程到此结束了,赶快参与星云Wiki的维护吧!

星云悬赏计划

星云漏洞悬赏计划

星云漏洞悬赏计划旨在提高整个星云生态的安全性,为构建星云良性生态提供保障。星云漏洞悬赏计划将为发现并提交漏洞的用户提供奖励,该计划由星云技术委员会组织实施,星云技术团队与社区用户共同参与。星云技术委员会鼓励社区用户按如下流程提交安全漏洞,并获得相应回馈,共同参与星云良性生态建设。

漏洞悬赏范围

漏洞悬赏范围包括星云主网、星云测试网、nebPay、Web钱包、neb.js等常规漏洞悬赏与合约间调用功能等专项漏洞悬赏。

漏洞评定标准

基于影响和可能性的OWASP风险评级模型计算的严重程度,星云技术委员会将根据漏洞的危害程度和可能的辐射范围两个维度评估奖励金额。最终奖励金额由星云技术委员会自行确定。

_images/bug-bounty-zh.png

危害程度:

  • 高:影响资产安全的漏洞
  • 中:影响系统稳定性的漏洞
  • 低:其它既不影响资产安全又不影响系统稳定性的漏洞

可能的辐射范围:

  • 高:所有人只要执行操作即可复现该漏洞,无论当前是否已经发现
  • 中:只有特定人群可复现(如只是开发者才可能遇到、普通用户不受影响的漏洞)
  • 低:覆盖面小于1%特定人群,如罕见Android机型;或个别偶发现象
漏洞奖励金额

为保证报告者获得稳定预期收益,悬赏奖励金额将以美元为标准发放等额NAS。 奖励金额分为5档:

  • 特别危险:1000+美元 + 上不封顶
  • 高危:500+美元
  • 中危:250 + 美元
  • 低危:100 + 美元
  • 改进:30+美元

注:星云测试网专项漏洞悬赏(如合约间调用功能测试网漏洞悬赏)价位等比上调,并以美元为标准发放等额NAS

漏洞提交入口

报告者需通过表单提交悬赏申请: 表单提交入口

注:

1.请确保填写内容准确性,悬赏奖金的发放将以此次表单提交内容为依据;

2.如有多人发现了相同的漏洞,最终以收到提交漏洞的先后次序为准,社区用户就漏洞问题的讨论不作为漏洞提交依据,仍需提交表单。

注意事项

1.星云漏洞悬赏计划长期有效,最终解释权归星云技术委员会所有,星云技术委员会保留单方面对本次漏洞悬赏计划的范围、标准、奖励金额等做出调整、取消的权利;

2.报告者提交悬赏申请后,星云技术委员会会进行确认并对提交漏洞进行跟进评估,评估时间将根据问题的严重程度及修复难度确定,评估结果会第一时间将以邮件形式反馈给对应报告者;

3.为避免漏洞被利用,请报告者将悬赏申请仅提交至漏洞悬赏入口;

4.报告者于发现漏洞至提交悬赏申请后的30天内负有保密义务,不得向任何第三方透露漏洞,该保密期限可以由星云单方面延长。如报告者违反保密义务,导致星云或其用户遭受任何损害的,应当赔偿所有相关损失;

5.星云技术委员会鼓励社区用户通过星云公共讨论组同星云技术团队、其他社区用户进行交流与讨论,也鼓励社区用户积极参与漏洞的修复。

FAQ

1.主网上线之后 DPoS会如何运作?是否会发布详细信息? 星云主要的共识算法是PoD(Proof of Devotion),在PoD上线之前,星云的过渡方案为DPoS(Delegated Proof of Stake)。等PoD算法经正式验证之后,我们再从DPoS过渡到PoD。

为了保护星云社区所有人的利益,为了让初生的星云链走得更稳健,未来可以更健壮,DPoS的所有超级节点都将由官方维护,暂不对外开放。我们将设立新的基金来管理过渡期的所有记账激励。这部分NAS不会在交易所出售,并将会全部用于早期社区建设,比如奖励星云链上的DApp开发者。我们会定期公示奖励发放详情。

2.如果有人在星云指数算法中发现了漏洞,并且故意作恶损坏网络,星云是否会惩罚这些人?

首先,NR在设计之初已经考虑到了用户作弊的可能性,以及对交易网络可能的影响,NR在设计上会尽力去避免这些作弊的可能性;其次,NR仅对一定时间跨度的交易进行评估,因此,一定程度上降低了作弊的长期影响;最后我们会引入NF,在NR不能应对用户作弊的情况下,及时对Nebulas进行升级,以保证NR的公平公正。

3.TPS是多少? 我们在测试中,最高得到了每秒打包2400个交易的测试结果。随着Go-Nebulas的优化,该数值应该还能有提升空间。

4.钱包的可用性怎样?这会是最酷的钱包吗?会有星云的搜索功能吗? 目前我们着重于钱包的基本功能,更多功能将会持续更新。

5.星云币是否会有去中心化的交易平台? 星云团队不会去主导星云币的去中心化交易,去中心化交易将会以去中心化组织的方式进行。

6.星云主网上线后是否可以挖矿? 星云主网上线后的一段时间之内将会采用DPoS共识机制,因此暂时还不能挖矿。在星云生态发展到一定规模,社区较为成熟之后,我们将从DPoS升级至PoD共识机制,届时将会开放挖矿功能。

7.TXhaush地址是什么? TXID就是我们常说的交易ID,可以用它来查看交易的最新状况。(这个地址是某次交易的数字签名,每笔交易对应一个唯一的TxHash,用这个可以查询交易双方的地址和交易数额,是区块链信息公开可查询可溯源的表现)

8.星云链上部署合约,要多少币,使用存储空间要多少币? 目前,部署是0.00000002nas左右,存储一个byte需要0.000000000001nas左右

9.我最近准备学习开发dapp,但我看github上面开发环境的配置只有linux和mac的,请问在windows上面难道就不可以开发了么? 虚拟机

10.我如何搭建星云开发环境?

您可以使用DOCKER快速搭建开发环境

如果您不会使用DOCKER,那么您可以使用星云钱包搭建(非官方教程)

接收地址需要填写的是星云主网钱包地址,您需要下载星云主网钱包获取星云主网钱包地址 下载地址

11.ATP和星云的关系? 星途协议 ATP 是星云实验室 Nebulas Labs 孵化的首个项目,定位为链上互动广告营销协议。ATP Smartdrop 的核心算法沿袭自较为成熟的 Nebulas Rank(星云指数)上。

版权

Nebulas Open Source Project License

The preferred license for the Nebulas Open Source Project is the GNU Lesser General Public License Version 3.0 (“LGPL v3”), which is commercial friendly, and encourage developers or companies modify and publish their changes.

However, we also aware that big corporations is favoured by other licenses, for example, Apache Software License 2.0 (“Apache v2.0”), which is more commercial friendly. For the Nebulas Team, we are very glad to see the source code and protocol of Nebulas is widely used both in open source applications and non-open source applications.

In this way, we are still considering the license choice, which kind of license is the best for nebulas ecosystem. We expect to select one of the LGPL v3, the Apache v2.0 or the MIT license. If the latter is chosen, it will come with an amendment allowing it to be used more widely.

Contributor License Agreement

All contributions to Nebulas wikis are licensed under the Creative Commons License SA 4.0.

For a complete list of everyone who contributed to the wiki, click here.