手把手教你星云DApp开发(第二部分)

作者/Yogi Xun

译者/李林璞(社区志愿者)

校对/陈达欣(社区志愿者)

编辑整理/星云团队

星云团队对参与翻译的社区成员表示感谢,并邀请更多人加入星云技术博客的翻译工作,有意者请在公众号后台留言。

往期教程:

手把手教你星云DApp开发(第一部分)

库与智能合约调用

我们已经发布了数个库来帮助大家更容易编写智能合约。这意味着你不需要从头开始来创建任何一个智能合约,因为我们已经帮你做了很多基本工作。

《如何在 Nebulas 上构建一个 DApp》的第二部分,我们将学习这些库,它们做了什么,以及如何使用它们。

BigNumber

BigNumber 模块构建于  [bignumber.js](https://github.com/MikeMcl/bignumber.js)之上,用来处理任意精度的十进制和非十进制运算。合约可以直接使用 BigNumber 来处理交易和其他转账操作中涉及到的数值计算。

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

Storage

storage 模块用来支持 Nebulas 上的数据持久化存储。功能上类似于传统的键值存储系统,当然存储不是免费的,需要消耗一定的 GAS。LocalContractStorage 是可以直接在合约中使用的内置storage对象,可存储的数据类型包括数字、字符串和 JavaScript 对象(需要序列化为字符串)。链上数据只能被存储它们的合约访问和修改。

基础

LocalContractStorage 支持三个操作:set、get`和 del,分别实现存储、读取和删除数据功能:

“use strict”;

var BankVaultContract = function () {

  // nothing

};

BankVaultContract.prototype = {

  init: function() {

      // nothing

  },

  set: function(name, value) { // name=”robin”, value=10000

      LocalContractStorage.set(“name”, name);

      // put 操作相当于 set

      LocalContractStorage.put(“value”, value);

  },

  get: function() {

      var name = LocalContractStorage.get(“name”);

      console.log(name);  // 打印 ‘robin’

      var value = LocalContractStorage.get(“value”);

     console.log(value); // 打印 ‘10000’

  },

  del: function() {

      var result = LocalContractStorage.del(“name”);

      console.log(result); // 打印 ‘robin’

      // delete 操作相当于 del

      result = LocalContractStorage.delete(“value”);

      console.log(result); // 打印 ‘10000’

      // 删除操作之后数据就不能被读取

  }

};

module.exports = BankVaultContract;

进阶

除上述基本用法之外,LocalContractStorage 还支持绑定以下两类链上存储空间到合约属性上:单值类型(storage property)和Map类型(storage map)。

· 单值存储(Storage Property)

只能存放一个值,对被绑定合约属性的读写都直接作用到LocalContractStorage 上。有两种方法可以定义这种绑定:

// 绑定一个单值存储空间到`obj` 上名为 `fieldName` 的属性 ,

// descriptor定义了这个属性的序列化方法。

//

// 默认的 descriptor实现是 JSON.parse() 和 JSON.stringify()。

// descriptor 为 ‘null’ 或 ‘undefined’时,默认 descriptor 将被使用。

// return this

defineProperty(obj, fieldName, [descriptor]);

// 批量绑定多个单值存储空间到 `obj`

// return this

defineProperties(obj, {

  fieldName1: descriptor1,

  fieldName2: descriptor2

});

通常我们会在初始化操作里完成绑定,如下所示:

“use strict”;

var BankVaultContract = function () {

   // 因为传值为 ‘null’,将会使用默认的 descriptor实现(序列化方法)

   LocalContractStorage.defineProperty(this, “name1”, null);

   // 一个自定义的 `descriptor` 实现

   // 在解析的时候返回 BigNumber 对象

   LocalContractStorage.defineProperty(this, “value1”, {

       stringify: function (obj) {

           return obj.toString();

       },

       parse: function (str) {

           return new BigNumber(str);

       }

   });

   // 用默认的序列化实现批量绑定

   LocalContractStorage.defineProperties(this, {

       name2: null,

       value2: null

   });

};

module.exports = BankVaultContract;

之后,读写绑定的属性就如同直接访问LocalContractStorage:

BankVaultContract.prototype = {

   init: function(name, value) { // name=”robin”, value=1

       this.name1 = name;

       this.value1 = value;

   },

   testStorage: function(name, value) { // name=”ROBIN”, value=2

       this.name2 = name;

       this.value2 = value;

       bool r = this.value1.lessThan(new BigNumber(0));

       console.log(this.name1 + “:” + r);           // robin:false

       console.log(this.name2 + “:” + this.value2); // ROBIN:2

   }

};

· Map存储(Storage Map)

Nebulas 存储支持Map数据结构,有 del/delete、get 和 set/put 这些操作,在遇到某些需要存储键值数据的场景时,就可以使用它。同样地,有两个方法来定义Map:

// 绑定单个map存储空间到名为`mapName`的合约属性,默认的 descriptor 实现和 defineProperty一样

// 返回 this

defineMapProperty(obj, mapName, [descriptor]);

// 批量绑定

// 返回 this

defineMapProperties(obj, {

   mapName1: descriptor1,

   mapName2: descriptor2

});

来看一个如何使用Map的例子:

‘use strict’;

var BankVaultContract = function () {

   LocalContractStorage.defineMapProperty(this, “userMap”);

   LocalContractStorage.defineMapProperty(this, “userBalanceMap”, {

       stringify: function (obj) {

           return obj.toString();

       },

       parse: function (str) {

           return new BigNumber(str);

       }

   });

   LocalContractStorage.defineMapProperties(this,{

       key1Map: null,

       key2Map: null

   });

};

BankVaultContract.prototype = {

   init: function () {

   },

   testStorage: function () {

       this.userMap.set(“robin”, “1”);

       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 = BankVaultContract;

Blockchain

Blockchain 模块用来获取当前正在执行的合约内的交易和区块信息。另外,还提供了若干有用的方法,诸如从合约账户中转出 NAS,进行地址格式验证等。

Blockchain 有两个属性:

1、block 执行合约的当前区块,它具有下列属性:

— timestamp 区块时间戳

— height 区块高度

2、transaction 执行合约的当前交易,它具有下列属性:

— hash 交易哈希值

— from 交易源地址

— to 交易目的地址,对于合约调用就是合约地址

— value 交易数值,字符串, 合约内用BigNumber存储计算

— nonce 交易的 nonce 值

— timestamp 交易时间戳

— gasPrice 交易的 gasPrice,字符串,合约内用 BigNumber 存储计算

— gasLimit 交易的 gasLimit,字符串,合约内用 BigNumber 存储计算

Blockchain 还提供了两个方法:

1、transfer(address, value) 将 NAS 从合约转出到address对应的账户。

· 参数 address:接收 NAS 的 Nebulas 账户地址

· 参数 value:转移数值,一个 BigNumber 对象

返回:0 – 转移成功,1 – 转移失败

2、verifyAddress(address) 验证参数 address 是否为一个有效的 Nebulas 地址。

返回:1 – 地址有效,0 – 地址无效

下面是用这个模块实现的简单实例:

‘use strict’;

var BankVaultContract = function () {};

BankVaultContract.prototype = {

   init: function () {

       console.log(‘init: Blockchain.block.height = ‘ + Blockchain.block.height);

       console.log(‘init: Blockchain.transaction.from = ‘ + Blockchain.transaction.from);

   },

   transfer: function (address, value) {

       var result = Blockchain.transfer(address, value);

       console.log(“transfer result:”, result);

   },

   verifyAddress: function (address) {

    var result = Blockchain.verifyAddress(address);

       console.log(“verifyAddress result:”, result);

   }

};

module.exports = BankVaultContract;

事件(Event)

Event 模块用来记录在合约执行过程中产生的事件。被记录的事件存储在链上的事件Trie结构中,可以通过事件查询方法 [rpc.getEventsByHash](https://github.com/nebulasio/wiki/blob/master/rpc.md#geteventsbyhash) 获取所有事件。通过`Event`模块输出的事件其最终Topic由用户自定义topic加固定前缀 chain.contract. 两部分构成 。使用方法如下:

Event.Trigger(topic, obj);

· topic:用户定义的topic

· obj:JSON 对象

下面是示例:

‘use strict’;

var BankVaultContract = function () {};

BankVaultContract.prototype = {

   init: function () {},

testEvent: function() {

       // 实际被存储的topic是“chain.contract.topic

       Event.Trigger(“topic“, {

   Data: {

value: “Event test.”

   }

       });

   }

};

module.exports = BankVaultContract;

控制台(Console)

console 模块提供了一个简单的调试控制台,类似于网页浏览器提供的 JavaScript 控制台。console 将把所有接收到的 args 以指定级别打印到 Nebulas Logger 上。

· console.log([…args<any>]) — — info 级别

· console.debug([…args<any>]) — — debug 级别

· console.warn([…args<any>]) — — warn 级别

· console.error([…args<any>]) — — error 级别

· console.info([…args<any>]) — — console.log() 别名

以上就是与智能合约相关的内置功能模块介绍。接下来,我们将简单说明下如何调用合约。

如何调用一个合约?

推荐的方法是:

首先通过启动本地节点加入 Nebulas 的主网或测试网,启动本地节点[快速入门](https://medium.com/nebulasio/dive-into-nebulas-2-quick-start-105da9df80e1)。

节点启动后,调用任何合约函数前首先使用 [unlockAccount()]

(https://github.com/nebulasio/wiki/blob/master/rpc_admin.md#unlockaccount)` 方法解锁账户:

// 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

   }

}

然后你就可以使用 [sendTransaction()](https://github.com/nebulasio/wiki/blob/master/rpc_admin.md#sendtransaction) 函数来执行智能合约调用了。

举个例子,调用前一个合约样例里的函数 testEvent():

// Request

curl -i -H ‘Accept: application/json’ -X POST http://localhost:8685/v1/admin/transaction -H ‘Content-Type: application/json’ -d ‘{“from”:”n1NZttPdrJCwHgFN3V6YnSDaD5g8UbVppoC”,”to”:”n1qsgj2C5zmYzS9TSkPTnp15bhCCocRPwno”, “value”:”100″,”nonce”:8,”gasPrice”:”1000000″,”gasLimit”:”2000000″,”contract”:{“function”:”testEvent”,”args”:”[]”}}’

// Result

{

“result”:{“txhash”:”b55358c2e12c1d48d4e6beaee7002a59138294fb2896ea8059ff5277553af59f”,”contract_address”:””}

}

有关 RPC 手册的更多信息,请参阅 [user API](https://github.com/nebulasio/wiki/blob/master/rpc.md) 和 [admin API](https://github.com/nebulasio/wiki/blob/master/rpc_admin.md)。

之后讲什么?

在即将发表的文章中,我们将分享智能合约的一个新功能,“accept” 函数。

关于星云链

星云链是新一代区块链公链,致力于构建可持续升级的良性生态。星云链具有三大理念:

 价值尺度:独创的区块链价值发现体系

星云指数(Nebulas Rank)衡量区块链世界多维度的数据价值。

◆ 自进化:避免硬分叉的自进化能力

星云原力(Nebulas Force)提供快速迭代、无需硬分叉的升级能力。

◆ 原生激励:前瞻性的激励和共识机制

星云激励(Nebulas Incentive)奖励对生态有贡献的开发者和用户。

星云链1.0鹰星云版具备以太坊所有功能,是比以太坊更优秀的区块链公链之选:

◆ 面向开发者友好,支持使用JavaScript编写智能合约和DApp,更易上手;

◆ 唯一使用LLVM的区块链公链,性能优越,采用并发技术,交易处理能力达到2000TPS,系统更加安全稳定,可扩展性强,且具备智能合约调用和协议升级能力。

详情请咨询:

官网:https://nebulas.io
Github:https://github.com/nebulasio/go-nebulas
Slack:https://nebulasio.herokuapp.com
Telegram中文群:https://t.me/nebulascn
Twitter:https://twitter.com/nebulasio

发表评论

电子邮件地址不会被公开。 必填项已用*标注

返回顶部