Develop applications using KV storage precompiled contracts

Tags: “Precompiled Contracts” “CRUD” “” Blockchain Applications “”


This article will introduce the KV storage function of FISCO BCOS 3.x to help developers develop block chain applications more efficiently and easily。

Special note: Solidity contracts that use KV to store precompiled contracts must be higher than version 0.6.0 and use ABIEncoderV2

KV storage usage

Currently, you can use the KV storage function in two ways: the KVTable contract and the Java SDK KVTable Service interface。

1. KVTable contract

-The Solidity contract only needs to introduce the Table.sol abstract interface contract file provided by FISCO BCOS。 -webankblockchain-liquid (hereinafter referred to as WBC-Liquid) contract can be used by declaring the interface of KVTable before implementing the contract。

Table contains a dedicated smart contract interface for distributed storage. The interface is implemented on a blockchain node. TableManager can create a dedicated KV table, and KVTable can be used as a table for get / set operations。The following are introduced separately。

1.1 TableManager contract interface

Used to create a KV table and open the KV table. The fixed contract addresses are ‘0x1002 (Solidity)’ and ‘/ sys / table _ manager (Liquid)’. The interfaces are as follows (only the interfaces related to KVTable are shown):

Interface Function Parameters Return value
createKVTable(string ,string) Create Table Table name, primary key name (currently only a single primary key is supported), field name The error code (int32) is returned. For details, see the following table
openTable(string) Get Table Address Table name. This interface is only used for Solidity Returns the real address of the table. If it does not exist, 0x0 is returned

1.2 The KVTable Contract

Used to access table data. The interface is as follows:

Interface Function Parameters Return value
get(string) Get Value primary key Return bool value and string. If the query fails, the first return value will be false
set(string,string) Set value Primary key, field value The error code (int32) is returned. If the error code is successful, 1 is returned. See the following table for other error codes

The interface returns an error code:

error code Description
0 Creation successful
-50001 Create table name already exists
-50002 Table name exceeds 48 characters
-50003 valueField field name length exceeds 64 characters
-50004 valueField Total field name length exceeds 1024 characters
-50005 keyField field value exceeds 64 characters in length
-50006 valueField field value length exceeds 16 megabytes
-50007 Duplicate field exists
-50008 Illegal character in field
其他 Other errors encountered while creating

With the above understanding of the KVTable abstract interface contract, the development of the KVTable contract can now be formally carried out。

2. Solidity contract uses KVTable

2.1 Introducing Table.sol

Place the Table.sol contract in the KVTableTest.sol peer directory and introduce Table.sol in the KVTableTest.sol contract file. The code is as follows:

pragma solidity >=0.6.10 <0.8.20;
pragma experimental ABIEncoderV2;

import "./Table.sol";

2.2 Create Table

In the KVTableTest.sol contract file, the core code for creating a table is as follows:

TableManager tm;
KVTable table;
string constant tableName = "t_kv_test";
event SetEvent(int256 count);
constructor () public{
  	/ / Create a TableManager object whose fixed address on the blockchain is 0x1002
    tm = TableManager(address(0x1002));

    / / Create the t _ kv _ test table. The primary key of the table is id, and other fields are item _ name
    tm.createKVTable(tableName, "id", "item_name");

    / / Get the real address, which is stored in the contract
    address t_address = tm.openTable(tableName);
    table = KVTable(t_address);
}

Note: This step is optional: for example, if the new contract only reads and writes the table created by the old contract, you do not need to create the table。

2.3 KV read and write operation for the table

In the KVTableTest.sol contract file, the core code for writing records is as follows:

function set(string memory id, string memory item_name)
public
returns (int32)
{
    int32 result = table.set(id,item_name);
    emit SetEvent(result);
    return result;
}

The core code of the read data record is as follows:

function get(string memory id) public view returns (bool, string memory) {
    bool ok = false;
    string memory value;
    (ok, value) = table.get(id);
    return (ok, value);
}

3. The WBC-Liquid contract uses the KVTable interface

3.1 Declaring the KVTable interface

Declare the KVTable interface before using it in the WBC-Liquid contract。

#![cfg_attr(not(feature = "std"), no_std)]

use liquid::storage;
use liquid_lang as liquid;
use liquid_lang::InOut;
use liquid_prelude::{string::String, vec::Vec};

#[derive(InOut)]
pub struct TableInfo {
    key_column: String,
    value_columns: Vec<String>,
}

/ / Interface declaration of TableManager
#[liquid::interface(name = auto)]
mod table_manager {
    use super::*;

    extern "liquid" {
        fn createKVTable(
            &mut self,
            table_name: String,
            key: String,
            value_fields: String,
        ) -> i32;
        fn desc(&self, table_name: String) -> TableInfo;
    }
}
/ / KVTable interface declaration
#[liquid::interface(name = auto)]
mod kv_table {

    extern "liquid" {
        fn get(&self, key: String) -> (bool, String);
        fn set(&mut self, key: String, value: String) -> i32;
    }
}

3.2 WBC-Liquid Create Table

The logic for creating a table can be implemented in the constructor of WBC-Liquid. The address of the TableManager introduced here is the BFS path ‘/ sys / table _ manager’. Note the difference between WBC-Liquid and Solidity。

The principle of creating a table is similar to that of Solidity, so I won’t repeat it again。

pub fn new(&mut self) {
    self.table_name.initialize(String::from("t_kv_test"));
  	/ / The fixed address of the TableManager is / sys / table _ manager
    self.tm
        .initialize(TableManager::at("/sys/table_manager".parse().unwrap()));
    self.tm.createKVTable(
        self.table_name.clone(),
        String::from("id"),
        String::from("item_name"),
    );
  	/ / After successful creation, / tables /+The table name created is the correct address for the contract
    self.table
        .initialize(KvTable::at("/tables/t_kv_test".parse().unwrap()));
}

3.3 KV read and write operation for the table

The main logic of writing is as follows:

pub fn set(&mut self, id: String, item_name: String) -> i32 {
    let count = (*self.table).set(id, item_name).unwrap();

    self.env().emit(SetEvent {
        count: count.clone(),
    });
    count
}

The main logic of reading is as follows:

pub fn get(&self, id: String) -> (bool, String) {
    if let Some((ok, value)) = (*self.table).get(id) {
        return (ok, value);
    }
    return (false, Default::default());
}

4. SDK KVTable Service interface

FISCO BCOS 3.x SDK provides KVTable Service data connection ports. These interfaces are implemented by calling a precompiled KVTable contract built into the blockchain to read and write user tables。The Java SDK KVTable Service is implemented in the org.fisco.bcos.sdk.v3.contract.precompiled.crud.KVTableService class. Its interfaces are as follows:

Interface Function Parameters Return value
createTable(String, String, String) Create Table Table name, primary key name, field name error code
set(String, String, String) Write data Table name, primary key name, field value error code
get(String, String) Read Data Table name, primary key name String
desc(String) Query table information Table Name KeyField and valueField for tables

The call to the write interface will generate the equivalent transaction to the call to the KV contract interface, which will not be stored until the consensus node consensus is consistent。