链上交互框架

MSP Serverless平台链上交互框架 #

框架方案 #

架构 #

MSP Serverless编程框架支持用户自定义provider,应用程序编译成的wasm也就是actor可以通过abi,以类似于本地函数调用的方式与provider进行交互。在本例中,我们实现了weelink的provider,使得wasm程序可以非常方便的与区块链进行交互。

Untitled

abi接口 #

定义weelink交互的abi接口,可以执行/查询任意的链上合约。abi具体定义如下:

invoke(method, args string)

发送交易接口,调用的链码函数名称以及对应的参数数组json marshal后的字符串

query(method, …args string)

发送交易接口,查询的链码函数名称以及对应的参数数组json marshal后的字符串

interface #

使用smithy定义上述abi,定义Invoke 和 Query的operation。然后使用wash工具生成rust的SDK库,这样provider和actor可以利用这个库来极大的简化代码。

/// The Weelink service has a single method, calculate, which
/// calculates the factorial of its whole number parameter.
@wasmbus(
    contractId: "wasmcloud:oneitfarm:weelink",
    actorReceive: true,
    providerReceive: true )
service Weelink {
  version: "0.1",
  operations: [ Invoke, Query ]
}

/// Invoke - Execute transaction
operation Invoke {
    input: InvokeRequest,
    output: InvokeResponse,
}

/// Query - Get info from contract.
operation Query {
    input: QueryRequest,
    output: QueryResponse,
}

provider #

对于provider,实现invoke和query的方法,向链上进行数据交互。其主要逻辑如下所示:

/// Handle Weelink methods
#[async_trait]
impl Weelink for FakePayProvider {
    async fn invoke(&self, ctx: &Context, arg: &InvokeRequest) -> RpcResult<InvokeResponse>{
        // ...
        let res = dyncallgo::call_invoke(method_ref, addr_ref, amount);
        match res {
            Ok(message) =>  Ok(InvokeResponse{success: true, message: Some(message.to_string()) }),
            Err(e) =>  Ok(InvokeResponse{success: false, message: Some(format!("{:?}", e)) }),
        }
    }
    /// Query - Get info from contract.
    async fn query(&self, ctx: &Context, arg: &QueryRequest) -> RpcResult<QueryResponse>{
        // ...
        let res = dyncallgo::call_query(method_ref, addr_ref);
        match res {
            Ok(message) =>  Ok(QueryResponse{success: true, message: Some(message.to_string()) }),
            Err(e) =>  Ok(QueryResponse{success: false, message: Some(format!("{:?}", e)) }),
        }
    }
}

actor #

actor可以实现任意的业务逻辑,这里我们实现一个测试的actor,当actor的invoke接口被调用时,actor就调用provider进行token transfer。当actor的query接口被调用时,actor查询参数指定的address的token余额。

其核心逻辑如下,判断接口是否是invoke,然后执行对应的逻辑:

/// Implementation of HttpServer trait methods
#[async_trait]
impl HttpServer for WeelinkActor {

    /// Returns a greeting, "Hello World", in the response body.
    /// If the request contains a query parameter 'name=NAME', the
    /// response is changed to "Hello NAME"
    async fn handle_request(
        &self,
        _ctx: &Context,
        req: &HttpRequest,
    ) -> std::result::Result<HttpResponse, RpcError> {
        info!("request arrived");
        let provider = WeelinkSender::new();
        if req.path.contains("invoke") {
            let invoke_request = &InvokeRequest{address, amount:count, method:"".to_string()};
            let res = provider.invoke(_ctx, invoke_request).await?;
            if  !res.success {
                /* handle not authorized */
                return Result::Err(RpcError::from(format!("invoke failed {}", res.message.unwrap()).to_string()));
            }
            message = res.message.unwrap().clone(); // todo change unwrap to match
        } else {
            let res = provider.query(_ctx, query_request).await?;
            if  !res.success {
                /* handle not authorized */
                return Result::Err(RpcError::from(format!("query failed {}", res.message.unwrap()).to_string()));
            }
            message = res.message.unwrap().clone(); // todo change unwrap to match
        }
        Ok(HttpResponse {
            body: message.as_bytes().to_vec(),
            ..Default::default()
        })
    }
}

模型试验 #

步骤 #

添加provider

Untitled

添加actor

Untitled

定义link

Untitled

Untitled

效果 #

对wasm代码进行调用,首先调用账户查看函数,然后调用账户交易函数,再次调用账户查看函数显示账户余额。

Untitled

wasmcloud弹性平台界面

Untitled

意义 #

本模型试验表明wasm代码可以借助provider与链上交互的功能,通过调用本地ABI函数的方式,获取与链上进行信息交互的能力,同时wasm作为一种程序,可以在其中实现任意的业务逻辑,作为打通链上链下交互的serverless桥梁。

基础知识 #

合约 #

weelink支持以太坊智能合约,因此本次测试采用标准ERC20合约进行模型试验。

rust调用go动态链接库 #

与链上交互流程由go语言实现,编程成动态链接库供rust使用。go源码目前在/Users/zc/Desktop/companyWorkspace/blockchain/chain_adapter/codetest/web3/eth/wasmclouddemo当中。

备注 #

由于go provider将在近期支持,而且rust链上操作的流程和库开发工作量大,对当前公司内部技术栈来说,维护心智也高一点。因此采用rust调用go动态链接库方式实现初版demo,后续切换到go provider。以tinygo方式实现actor,降低程序编写门槛。

参考 #

使用rust调用go动态库

libloading rust库

Rust 使用 libloading 的入门笔记

cgo result has go pointer

Rust 与 C 语言字符串的相互转换

程序源码已经放到gitlab