In depth explanation of the development of blockchain alliance platform wasm contract

Universal blockchain 2022-04-04 02:07:56 阅读数:625

depth explanation development blockchain alliance

League chain PlatONE Support WASM virtual machine . This article explains the functions of the contract through a series of code examples , You can learn these examples to gain an in-depth understanding of how to write an application contract . obtain PlatONE Code

Contract class

Contract Class has bcwasm The contract base class provided by the library , The user development contract of this class must be derived from .Contract One is defined in the class init() Virtual functions , The user contract needs to implement this init() function , This function is executed when the contract is first published , Call only once , This method works similar to solidity Constructor in contract .

Be careful : init() Method must be implemented as public type , In this way, the function can be called to initialize the contract data when the contract is deployed .

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
class my_contract : public bcwasm::Contract
{
public:
my_contract(){}
/// Implement the parent class : bcwasm::Contract The virtual function 
/// This function is executed when the contract is first published , Call only once 
void init() {
/* Do some initialization */
}
};
}

Contract external methods

Contract external method refers to the interface that the contract can call externally , The function is similar to solidity In the contract public Type method , stay bcwasm The library is through BCWASM_ABI Macro to define external methods . adopt BCWASM_ABI Declared method , Can be passed outside the contract rpc Message call 、 It can also be called by other contracts .

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
class my_contract : public bcwasm::Contract
{
public:
my_contract(){}
/// Implement the parent class : bcwasm::Contract The virtual function 
/// This function is executed when the contract is first published , Call only once 
void init() {
/* Do some initialization */
}
void setData(char * data){
/* Modify contract data */
}
};
}
// External methods 
BCWASM_ABI(my_namespcase::my_contract:setData)

On chain storage interface

Wasm The contract built-in library provides for data persistence setState() Method , You can call bcwasm::setState() Function to realize data persistence , Query and call... Accordingly bcwasm::getState().

In the example contract below , There are two interfaces for external calls setData(char* data) and getData(). These two methods call bcwasm::setState()bcwasm::getState(), In order to realize the uplink persistence and query of data .

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
class my_contract : public bcwasm::Contract
{
public:
void init(){}
void setData(char * data){
std::string m_data(data);
bcwasm::setState("DataKey", m_data);
}
const char* getData() const{
std::string m_data;
bcwasm::getState("DataKey", m_data);
return m_data.c_str();
}
};
}
// External methods 
BCWASM_ABI(my_namespcase::my_contract, setData)
BCWASM_ABI(my_namespcase::my_contract, getData)

Const Method

In the contract const The type method provides read-only operations on contract status , The function declared by this type cannot modify contract data , It is generally used to query the data on the contract chain . In the code below ,getData() That is to say const Method , Used to query data .

const char* getData() const{
std::string m_data;
bcwasm::getState("DataKey", m_data);
// Read the contract data and return 
return m_data.c_str();
}

Struct、Map

Struct Structure

Structural grammar rules and C++ Agreement , However, if it is used, the structure data needs to be chained and persisted , You need to use... In the structure BCWASM_SERIALIZE macro , This macro provides serialization for struct types / The way of deserialization .

In the contract example below , Defined a Student_t Type of structure , And through the contract interface setData() Persist data to the chain , And then you can go through getData() Method query data .

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
struct Student_t {
std::string name; // full name 
int64_t age; // Age 
BCWASM_SERIALIZE(Student_t, (name)(age));
};
class my_contract : public bcwasm::Contract
{
public:
void init(){}
void setData(char * name, int64_t age){
Student_t stu;
stu.name = std::string (name);
stu.age = age;
bcwasm::setState("DataKey", stu);
}
const char* getData() const{
Student_t stu;
bcwasm::getState("DataKey", stu);
std::stringstream ret;
ret << "stu.name: " << stu.name << ", stu.age: " << stu.age;
// Read the contract data and return 
return ret.str().c_str();
}
};
}
// External methods 
BCWASM_ABI(my_namespcase::my_contract, setData)
BCWASM_ABI(my_namespcase::my_contract, getData)

Map

bcwasm Provided in map Type of packaging , Definition map When the structure , You need to specify the map The name of 、key The type of 、value The type of .

char mapName[] = "students";
bcwasm::db::Map<mapName, std::string, Student_t> students;

map The structure supports the following api:

  • find(key): according to key lookup value
  • insert(key, value): When map There is no such thing as key When indexing the contents of , Insert to key For index value
  • update(key, value): When map Already exists in key When indexing the contents of , to update key Corresponding value

The example contract below defines a map Used to save the student's name 、 Age information , Take the student's name as key As index , among setData Method enter the student's name 、 Age ,getData Query method based on student's name .

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
struct Student_t {
std::string name; // full name 
int64_t age; // Age 
BCWASM_SERIALIZE(Student_t, (name)(age));
};
// Define a map, Save student name 、 Age information , Take the student's name as key As index 
char mapName[] = "students";
bcwasm::db::Map<mapName, std::string, Student_t> students;
class my_contract : public bcwasm::Contract
{
public:
void init(){}
void setData(char * name, int64_t age){
Student_t stu;
stu.name = std::string (name);
stu.age = age;
Student_t *stu_p = students.find(std::string(name));
if (stu_p == nullptr){
students.insert(stu.name, stu);
} else{
students.update(stu.name, stu);
}
}
const char* getData(char* name) const{
Student_t *stu = students.find(std::string(name));
if (stu == nullptr){
return (std::string("no such student")).c_str();
}else{
std::stringstream ret;
ret << "stu.name: " << stu->name << ", stu.age: " << stu->age;
return ret.str().c_str();
}
}
};
}
// External methods 
BCWASM_ABI(my_namespcase::my_contract, setData)
BCWASM_ABI(my_namespcase::my_contract, getData)

Event

Event Allow us to easily use PlatONE Log infrastructure for . We can do it in dapp In the listening Event, When the contract produces Event when , Will cause the relevant parameters to be stored in the transaction Log in . these Log Associated with address , Written into the blockchain , You can trade Receipt Inquire about... Generated by an exchange Event.

macro BCWASM_EVENT and BCWASM_EMIT_EVENT Provides for the contract Event Direct support for , How to use it is as follows :

/// Definition Event.
/// BCWASM_EVENT(eventName,arguments...)
BCWASM_EVENT(setData,const char *,const int64_t)
/// Trigger Event
BCWASM_EMIT_EVENT(setData,name,age);

Let's add... To the sample contract Event event , Every time you call setData() when , Trigger Event event , The sample contract code is as follows :

#include <bcwasm/bcwasm.hpp>
namespace my_namespcase {
struct Student_t {
std::string name; // full name 
int64_t age; // Age 
BCWASM_SERIALIZE(Student_t, (name)(age));
};
// Define a map, Save student name 、 Age information , Take the student's name as key As index 
char mapName[] = "students";
bcwasm::db::Map<mapName, std::string, Student_t> students;
class my_contract : public bcwasm::Contract
{
public:
void init(){}
// Definition Event
BCWASM_EVENT(setData,const char*,int64_t)
void setData(char * name, int64_t age){
Student_t stu;
stu.name = std::string(name);
stu.age = age;
Student_t *stu_p = students.find(std::string(name));
if (stu_p == nullptr){
students.insert(stu.name, stu);
} else{
students.update(stu.name, stu);
}
/// Trigger Event
BCWASM_EMIT_EVENT(setData,name,age);
}
const char* getData(char * name) const{
Student_t *stu = students.find(std::string(name));
if (stu == nullptr){
return (std::string("no such student")).c_str();
}else{
std::stringstream ret;
ret << "stu.name: " << stu->name << ", stu.age: " << stu->age;
return ret.str().c_str();
}
}
};
}
// External methods 
BCWASM_ABI(my_namespcase::my_contract, setData)
BCWASM_ABI(my_namespcase::my_contract, getData)

Join in Event Then we call again setData contract , Then query the transaction Receipt, As shown below .

{
blockHash: "0xd3324a86bb4c2a9f99592ea16c02bddae6ced421c0170a07f781fb9dfa7b1d8c",
blockNumber: 77,
contractAddress: null,
cumulativeGasUsed: 449872,
from: "0x61eaf416482341e706ff048f20491cf280bc29d6",
gasUsed: 449872,
logs: [{
address: "0x07894a9f9edffe4b73eb8928f76ee2993039e4d7",
blockHash: "0xd3324a86bb4c2a9f99592ea16c02bddae6ced421c0170a07f781fb9dfa7b1d8c",
blockNumber: 77,
data: "0xc785676578696e1c",
logIndex: 0,
removed: false,
topics: ["0xd20950ab1def1a5df286475bfce09dc88d9dcba71bab52f01965650b43a7ca8e"],
transactionHash: "0xa4735b9dbf93f0f8d7831f893270ff8a42244141455ed308fd985b90ee9bc3f5",
transactionIndex: 0
}],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000800000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000",
status: "0x1",
to: "0x07894a9f9edffe4b73eb8928f76ee2993039e4d7",
transactionHash: "0xa4735b9dbf93f0f8d7831f893270ff8a42244141455ed308fd985b90ee9bc3f5",
transactionIndex: 0
}

stay Receipt Of logs In the field, we pass Event Data generated , The meaning of the main fields is :

  • address: Produce this Event The contract address of
  • blockHash: Produce this Event The exchange is in the block
  • blockNumber: Produce this Event The exchange is in block number
  • data: Event Parametric Rlp code , In the contract example above, it is [name,age] Of RLP code
  • topics: Event Hash value of name , The contract example above is a string "setData" Hash value of

Cross contract call

bcwasm The library provides classes DeployedContract Used to call across contracts , When other contracts need to be invoked in the contract, , First initialize a contract with the target contract address DeployedContract Example , Then call the corresponding method , As shown below :

 

// Call destination address : "0x07894a9f9edffe4b73eb8928f76ee2993039e4d7"
// Method called : setData(name,age)
bcwasm::DeployedContract regManagerContract("0x07894a9f9edffe4b73eb8928f76ee2993039e4d7");
char name[]= "name";
int64_t age = 18;
regManagerContract.call("setData", name, age);

DeployedContract The following call modes are provided :

 

// Call... Without return value 
void call("funcName", arguments...);
void delegateCall("funcName", arguments...);
// string Type return value 
std::string callString("funcName", arguments...) std::string delegateCallString("funcName", arguments...) // Int64 Type return value  int64_t callInt64("funcName", arguments...) int64_t delegateCallInt64("funcName", arguments...)

call() And delegateCall() Can be used to call the contract , There are differences in the perspective of the contract , Use call() The caller seen in the transferred contract caller Is the contract that initiated the call , And when you use delegateCall() when , The contract that initiates the call will directly transfer its own caller Pass on to the target contract . For example, in the following two examples , In the first case ,ContractB What you see caller yes ContractA The address of ; In the second case ,ContractB What you see caller yes user The address of .

1. user ----> ContractA --call()--> ContractB
2. user ----> ContractA --delegateCall()--> ContractB

Register in initialization method cns contract

PlatONE ... is provided in the system contract CNS service function , The contract can be registered in the system contract , To call the contract using the contract name version without using an address . You can use the initialization method of the contract init() Directly register the contract in the system contract , For use CNS The convenience of the contract .

By means of init() Call in method cnsManager The contract cnsRegisterFromInit(name,version) Method can be implemented , It should be noted that the contract version must be "x.x.x.x" The format of .

void init() {
DeployedContract reg("0x0000000000000000000000000000000000000011");
reg.call("cnsRegisterFromInit", "name", "1.0.0.0");
}

hash()

bcwasm The library provides a hash method consistent with Ethereum sha3(), Use as follows :

std::string msg = "hello";
bcwasm::h256 hash = bcwasm::sha3(msg);

ecrecover()

ecrecover() It provides the function of recovering the signature and check-out address of the celebrity according to the original text , How to use it is as follows :

// For Strings "hello" The signature of the 
std::string sig = "4949eb47832d8a90c8c94b57de49d11b031fcd6d6dcb18c198103d2d431e2edf07be6c3056fe054ad6d1f62a24a509426a1c76687708ab684ad609ae879399fa00";
// Original signature 
std::string msg = "hello";
// First, find the hash of the original signature 
bcwasm::h256 hash = bcwasm::sha3(msg);
// adopt ecrecover Restore the address of the drawer 
bcwasm::h160 addr = bcwasm::ecrecover(hash.data(), bcwasm::fromHex(sig).data());

caller()、origin() and address()

  • caller(): return caller Information , If the contract A adopt call() Method call contract B, that caller It's a contract A The address of
  • origin(): Return the address of the originator , Regardless of the call between contracts , This function always returns the address of the sender of the original transaction
  • address(): Return the address of the current contract

Other matters needing attention

  1. The current contract external interface only supports the following data types :

    char/char* /const char*/char[]
    unsigned __int128/__int128/uint128_t
    unsigned long long/uint64_t
    unsigned long/uint32_t
    unsigned short/uint16_t
    unsigned char/uint8_t
    long long/int64_t
    long/int32_t/int
    short/int16_t
    char/int8_t
    void 

     

  2. platone Contract library pair u32 And fixed length arrays bytesN Undefined , At present, we can use uint32_t and char[] Array instead of .

  3. When the query method of the contract external interface is , If function returns a string ( For example, by calling string Of c_str() Method ), A new application needs to be made inside this function (malloc) A piece of memory , And the string copy To this new memory , Because the memory is made up of BCWasm Unified management of virtual machines , Therefore, there is no memory leakage problem . When returning string type , have access to RETURN_CHARARRAY Macro implementation , The macro is defined as follows

    #define RETURN_CHARARRAY(src,size) \ do \ { \ char *buf = (char *)malloc(size); \ memset(buf,0,size); \ strcpy(buf,src); \ return buf; \ } \ while(0)

     

  4. wasm In the contract built-in library u256 Type conversion string type requires the following calls :

    u256value.convert_to<std::string>()

     

版权声明:本文为[Universal blockchain]所创,转载请带上原文链接,感谢。 https://netfreeman.com/2021/01/20210121182202344a.html