Nebulas 101 – 04 智慧合約儲存區 –》星雲鏈開發

NO IMAGE

Nebulas 3200 萬獎金的 DApp 開發激勵計劃

註冊地址https://incentive.nebulas.io/cn/signup.html?invite=58jke

時間: 2018 年 5 月 7 日 ~ 7 月 2 日

每提交一個有效 DApp 即可獲得 100 NAS ( 7000 元)

會的話 可以試試!專為咱們 Javascript 提供哈,返的獎金可平分返給你!

QQ群交流:613121183

—————————————————————-

Nebulas 101 – 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,分別是arrayMap,dataMap,arrayMap採用嚴格遞增的計數器作為key,dataMap採用data的key作為key,詳細參見set方法。遍歷實現參見forEach,先遍歷arrayMap,得到dataKey,再對dataMap遍歷。Tip:由於Map遍歷效能開銷比較大,不建議對大資料量map進行遍歷,建議按照limit,offset形式進行遍歷,否者可能會由於資料過多,導致呼叫超時。

##### 遍歷map資料
"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和星雲鏈互動