Nebulas 101 - 04 Armazenamento de Smart Contracts

Tutorial YouTube

Antes discutiu-se como escrever smart contracts e como os implementar e invocar na Nebulas.

Agora vamos introduzir em detalhe o armazenamento de um smart contract. Os “contractos inteligentes” de Nebulas fornecem capacidades de armazenamento de dados on-chain (na blockchain). Semelhantes ao sistema de armazenamento key-value tradicional (ex: redis), smart contracts podem ser armazenados em Nebulas ao pagar com gás (gas).

LocalContractStorage

O ambiente de armazenamento de Smart Contract de Nebulas vem com armazenamento de objectos embutido, LocalContractStorage, que pode armazenar números, cadeias de caracteres (strings), e objectos JavaScript. Dados armazenados apenas podem ser usados em smart contracts. Outros contractos não serão capazes de ler dados armazenados.

Básicos

O API LocalContractStorage inclui set, get e del, que permitem-lhe guardar, ler, e apagar dados. O armazenamento pode conter números, strings, e objectos.

Armazenar Dados de LocalContractStorage

// armazenar dados. Os dados vão ser guardados como strings JSON
LocalContractStorage.put(key, value);
// Ou
LocalContractStorage.set(key, value);

Ler Dados de LocalContractStorage

// obtém o valor da chave
LocalContractStorage.get(key);

Apagar Dados de LocalContractStorage

// apagar dados, dados não podem ser lidos após a sua eliminação
LocalContractStorage.del(key);
// Ou
LocalContractStorage.delete(key);

Exemplos:

'use strict';

var SampleContract = function () {
};

SampleContract.prototype = {
    init: function () {
    },
    set: function (name, value) {
        // Armazenar uma string
        LocalContractStorage.set("name",name);
        // Armazenar um número (valor)
        LocalContractStorage.set("value", value);
        // Armazenar um objecto
        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;

Avançado

Além dos métodos básicos set, get, and del, LocalContractStorage também fornece métodos para vincular propriedades de smart contracts. Pode ler e escrever propriedades vinculadas directamente sem invocar interfaces LocalContractStorage para get e set.

Propriedades Vinculadas

Instância de um objecto,nome de campo, e descritor devem ser fornecidos para vincular propriedades.

Interface de Vinculação
// define a propriedade do objecto chamada `fieldname` para `obj` com descritor.
// descritor padrão é o descritor JSON.parse/JSON.stringify.
// retorna
defineProperty(obj, fieldName, descriptor);

// define propriedades do objecto para `obj` de `props`.
// descritor padrão é o descritor JSON.parse/JSON.stringify.
// retorna
defineProperties(obj, descriptorMap);

Eis um exemplo para vincular propriedades num smart contract:

'use strict';

var SampleContract = function () {
    // O tamanho da propriedade `size` do SampleContract é uma propriedade armazenada. Lê e escreve para ` size` e será armazenada na chain.
    // O `descritor` está definido como nulo aqui, o JSON.stringify () e JSON.parse () padrão serão utilizados.
    LocalContractStorage.defineMapProperty(this, "size");

    // O valor da propriedade `value` do SampleContract é uma propriedade armazenada. Lê e escreve para `value` e será armazenada na chain.
    // Aqui está uma implementação de `descritor` personalizada, armazenada como uma string, e a retornar o objecto BigNumber durante a análise. 
    LocalContractStorage.defineMapProperty(this, "value", {
        stringify: function (obj) {
            return obj.toString();
        },
        parse: function (str) {
            return new BigNumber(str);
        }
    });
    // Múltiplas propriedades do SampleContract estão definidas como propriedades de armazenamento em quantidade/lotes, e os descritores correspondentes usam serialização JSON por padrão
    LocalContractStorage.defineProperties(this, {
        name: null,
        count: null
    });
};

module.exports = SampleContract;

Depois, pode ler e escrever essas propriedades directamente como o seguinte exemplo:

SampleContract.prototype = {
 // Usado quando o contracto é implementado inicialmente, e não pode ser usado uma segunda vez
 init: function (name, count, size, value) {
 // Armazena os dados na chain ao implementar o contracto
 this.name = name;
 this.count = count;
 this.size = size;
 this.value = value;
 },
 testStorage: function (balance) {
 // O valor será lido do armazenamento de dados na chain, e automaticamente convertido num conjunto BigNumber de acordo com o descritor
 var amount = this.value.plus(new BigNumber(2));
 if (amount.lessThan(new BigNumber(balance))) {
 return 0
 }
 }
};

Vinculação de Propriedades do Mapa

E mais, LocalContractStorage também fornece métodos mpara vincular propriedades do mapa. Eis um exemplo para vincular propriedades do mapa e usá-las num smart contract.

'use strict';

var SampleContract = function () {
    // Define a propriedade do `SampleContract` para `userMap`. Dados do mapa podem depois ser armazenados na chain usando `userMap`
    LocalContractStorage.defineMapProperty(this, "userMap");

    // Define a propriedade do `SampleContract` para `userBalanceMap`, e define o armazenamento e serialização das funções de leitura.
    LocalContractStorage.defineMapProperty(this, "userBalanceMap", {
        stringify: function (obj) {
            return obj.toString();
        },
        parse: function (str) {
            return new BigNumber(str);
        }
    });

    // Define as propriedades do `SampleContract` para vários lotes de mapas
    LocalContractStorage.defineMapProperties(this,{
        key1Map: null,
        key2Map: null
    });
};

SampleContract.prototype = {
    init: function () {
    },
    testStorage: function () {
        // Armazena os dados em userMap e serializa os dados na chain
        this.userMap.set("robin","1");
        // Armazena os dados em userBalanceMap e guarda os dados na chain usando uma função de serialização personalizada
        this.userBalanceMap.set("robin",new BigNumber(1));
    },
    testRead: function () {
        // Lê e armazenada dados
        var balance = this.userBalanceMap.get("robin");
        this.key1Map.set("robin", balance.toString());
        this.key2Map.set("robin", balance.toString());
    }
};

module.exports = SampleContract;
Iterar mapa

No contracto, mapa não suporta iteradores. Se precisar de iterar o mapa, pode usar a seguinte maneira: defina dois mapas, arrayMap e dataMap. ArrayMap com um contador estrictamente crescente como chave, e dataMap com a chave de dados como chave.

"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;
    },
    
    iterate: 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;

Próximo passo: Tutorial 5

Interação com Nebulas por API RPC