文档

合约间调用

更新时间:

在合约执行过程中,合约平台允许该合约调用其他合约。本文先对合约运行的上下文环境进行介绍,然后通过示例来讲解合约间普通调用的方法。

合约运行的上下文环境

合约上下文指的是合约运行环境。例如,谁调用了这个合约(sender),本合约的ID是什么(self),当前合约能使用的 gas 是多少,当前合约本次收到多少资产。 这些信息可以通过以下 API 获取到:

  • Identity GetSender();

    返回一个Identity类型的数据,该数据表示本次调用是谁发起的,消耗的gas是由发起者提供的。

  • uint64_t GetValue();

    返回一个uint64_t类型的数据,该数据表示本次调用调用者给了本合约多少资产。

  • uint64_t GetGas();

    返回一个uint64_t类型的数据,该数据表示当前合约还剩余多少可用gas。

  • Identity GetSelf();

    返回一个Identity类型的数据,该数据表示返回本合约的唯一标识-Identity。

  • Identity GetOrigin();;

    返回交易发起者的id。这是一个不随着调用深度变化的值,例:账户alice调用了A合约,A合约调用了B合约,B合约调用了C合约…,无论调用层次多深,执行GetOrigin()得到的都是alice的账户Identity。

普通调用

普通调用指在发生 CallContract 函数调用时,当前合约的上下文被保存,智能合约平台随后切换到被调用合约的上下文环境并执行相关代码。例如 A 合约(简称“A”)调用 B 合约(简称“B”)的某个方法 f,则在 f 执行过程中,上下文环境切换到 B 合约的环境,其对存储的影响体现在 B 合约相关的成员变量存储中。当普通调用发生时,需要指定被调用合约的Identity,转移给被调用合约的资产数量,gas数量,被调用合约接口名称,被调用合约接口的参数列表。

  • A->B

    A 合约调用 B 合约。B 执行过程中,GetSender() 是 A;B 的代码操作的都是 B 合约本身的存储。GetSelf() 得到的是 B 的合约 ID。

  • A->B->C

    A 合约调用 B 合约;B 合约调用 C 合约。B 执行过程中,GetSender() 是 A,GetSelf() 是 B;C 执行过程中,GetSender() 是 B,GetSelf() 是 C。

普通调用使用CallContract,基本形式为:

auto ret = CallContract<TYPE>(contractId, methodName, value, gas, args...);

其中TYPE是被调用合约方法的返回值类型。ret 是一个自动模板结构,其定义如下:

template<class TYPE>
struct {
    int code;   //如果被调用合约没有异常终止,则该值为0,否则为1
    std::string msg;    //如果被调用合约异常终止,则该值就是异常的消息内容;如果被调用合约没有出现异常,该值为空字符串
    TYPE result;    //如果合约没有出现异常,该值是被调用方法的返回值;若出现了异常,该值无意义。若TYPE为void,则没有该字段
};
重要

  • CallContract 不支持指针类型作为参数或引用类型作为返回值,否则会导致合约编译错误。

  • 不要出现环形调用,即A->B->…->B,若出现环形调用,第一次调用B,也许修改了B中某些值,第二次调用B合约感知不到第一次调用造成的修改。这种情况很难精准预测B合约的行为,请尽量避免。

示例:

/*A合约*/
#include <mychainlib/contract.h>
using namespace mychain;
class A: public Contract {
public:
    uint32_t last_value;
    /* A合约调用B合约,已知B合约的id为b */
    INTERFACE void CallB() {
        last_value = 100;
        Identity b = "0011223344556677889900112233445566778899001122334455667788990000";
        auto ret = CallContract<uint32_t>(b/*contractID*/, "add"/*methodName*/, 0/*value*/,10000/*gas*/, 11/*x*/, 12/*y*/);
        // B合约中last_value的值为33,A合约中的last_value依然为100
        //ret.result的值为B合约的add方法的返回值,即1234
    }
};

INTERFACE_EXPORT(A,(CallB));
/*B合约*/
#include <mychainlib/contract.h>
using namespace mychain;
class B: public Contract {
    uint32_t last_value;
public:
    INTERFACE uint32_t add( uint32_t x, uint32_t y) {
        last_value = x + y;
        return 1234;
    }
};
INTERFACE_EXPORT(B, (add))