本文汇总了 C++ 合约开发过程中合约平台所支持的合约 API,并通过示例对合约开发过程中会用到的内置类库(时间格式化、第三方库)进行介绍。
如果没有特殊说明,则该API在非TEE和TEE版本中都支持。也许您可以看到以下API调用了一些更加基础的库函数,但请您不要擅自使用那些函数,因为如果使用不当可能造成您的合约行为不符合预期。请只使用本文档中列出的API。
合约 API 根据功能主要分为以下 4 类:
区块链交互 API
bool CheckAccount(const Identity& id/*in*/);
检查当前状态下,id
指定的账号是否存在。
请求参数:
参数 | 类型 | 说明 |
id | Identity | 要查询的账号 ID |
返回值:
参数 | 类型 | 说明 |
result | bool | 账号 ID 存在则返回 true,否则返回 false |
uint64_t GetBlockNumber();
获取上一个区块的区块号。
返回值:
参数 | 类型 | 说明 |
result | uint64_t | 最新已经形成的区块号 |
int GetBlockHash(uint64_t block_number/*in*/, std::string& block_hash/*out*/);
获取指定区块的hash。
请求参数:
参数 | 类型 | 说明 |
block_number | uint64_t | 要查询的区块号 |
block_hash | std::string& | 获取到的区块hash,原始字节流(注意不是16进制格式); 若查询的区块不存在,则block_hash的值维持原值 |
返回值:
参数 | 类型 | 说明 |
result | int | 如果要查询的区块不存在,则返回1,否则返回0 |
uint64_t GetBlockTimeStamp();
获取上一个区块的时间戳。
返回值:
参数 | 类型 | 说明 |
result | uint64_t | 最新已经形成的区块的时间戳,单位:ms |
Identity GetOrigin();
获取交易的发起者的 ID。
返回值:
参数 | 类型 | 说明 |
result | Identity | 出参,本次合约被调用的交易发起者 ID,参考 合约运行的上下文环境。 |
int GetAuthMap(const Identity& id/*in*/, std::map<std::string,uint32_t>& auth);
获取账号权限列表。
请求参数:
参数 | 类型 | 说明 |
id | Identity | 入参,要查询的账号 ID |
auth |
| 出参,获取到的 <公钥,权重> map,如果返回非 0, 注意:公钥不是16进制格式。 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 指定的账号不存在或被冻结,返回 1;否则返回 0。 |
int GetBalance(const Identity& id/*in*/, int64_t& value/*out*/);
获取指定账号的余额。
请求参数:
参数 | 类型 | 说明 |
id | const Identity& | 账号 ID |
value | uint64_t& | 该账号的余额。若返回值不为0,value维持原值。 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 对应的账户不存在或该账户被冻结则返回1,否则返回0。 |
int GetCode(const Identity& id/*in*/, std::string& code/*out*/);
获取指定合约的代码。
请求参数:
参数 | 类型 | 说明 |
id | const Identity& | 合约 ID |
code | std::string& | 获取到的合约的代码,若返回值不为0,code维持原值。若得到code值超过1M,则会抛出异常信息 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 对应的账户不存在或该账户被冻结则返回1,否则返回0。 |
int GetCodeHash(const Identity& id/*in*/, std::string& hash/*out*/);
获取指定合约的代码 hash。
请求参数:
参数 | 类型 | 说明 |
id | const Identity& | 账号 ID |
hash | std::string& | 合约的代码hash,原始字节串,非16进制字符串。 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 对应的账户不存在或该账户被冻结则返回1,否则返回0。 |
int GetRecoverKey(const Identity& id/*in*/, std::string& recover_key/*out*/);
获取指定账号的恢复公钥。
请求参数:
参数 | 类型 | 说明 |
id | const Identity& | 账号 ID |
recover_key | std::string& | 账户的恢复公钥,若函数返回非0,参数recover_key维持原值 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 对应的账户不存在或该账户被冻结则返回1,否则返回0。 |
int GetAccountStatus(const Identity& id/*in*/, uint32_t& status/*out*/);
获取指定账号的状态。
请求参数:
参数 | 类型 | 说明 |
id | int | 账号 ID |
status | uint32_t | 账号的状态代码,若函数返回非 0, 则参数 |
返回值:
参数 | 类型 | 说明 |
result | int | 若 ID 对应的账号不存在返回 1,否则返回 0。 |
std::string GetTxHash();
获取交易的 hash(触发交易的 hash)。
返回值:
参数 | 类型 | 说明 |
result | std::string | 本合约触发交易的hash,原始字节串,非16进制格式。 |
int TransferBalance(const Identity& to/*in*/, int64_t balance/*in*/);
向指定账户转移资产。
请求参数:
参数 | 类型 | 说明 |
to | const Identity& | 资产接收者的地址 |
balance | int64_t | 要转移的资产数量 |
返回值:
参数 | 类型 | 说明 |
result | int | 转移成功返回 0,否则返回 1。 |
int Revert(const std::string& exception/*in*/);
立即终止该合约的运行,并给区块链发送信号,附带 exception
内容,表明该合约被异常终止。
请求参数:
参数 | 类型 | 说明 |
exception | const std::string& | 报出的错误信息,该自动会被存放到交易回执的output字段中 |
返回值:
参数 | 类型 | 说明 |
result | int | 恒为 0 |
int Require(bool condition/*in*/, const std::string& exception/*in*/);
若条件
condition
值为false
,则调用Revert
。if (!condition) { Revert(exception); }
请求参数:
参数 | 类型 | 说明 |
condition | bool | 需要判断的条件 |
exception | const std::string& | 报出的错误信息,该自动会被存放到交易回执的output字段中 |
返回值:
参数 | 类型 | 说明 |
result | int | 恒为 0 |
template <class T>int Log(const T& data/*in*/, const std::vector<std::string>& topics/*in*/);
产生通知事件,data字段会做序列化, 每个topic会以十六进制字符串的格式记录在交易回执中。
请求参数:
参数 | 类型 | 说明 |
data | const T& | 用户自定义日志内容,仅支持 可序列化数据类型。 |
topics | std::vector & | 事件主题,其序列化后的大小不能大于1MB,否则会返回失败,事件不会记录在交易回执中。 |
返回值:
参数 | 类型 | 说明 |
result | int | 失败返回1,成功返回0 |
使用示例如下:
std::string topic1 = "good";
std::string topic2 = "morning";
Log("hi"s, {topic1, topic2});
// 上链后对应字段为:
// "data":"026869"
// "topics":["676f6f64","6d6f726e696e67"],
// data字段会先做序列化, "hi"序列化后, 对应"026869"
// 针对topic字段, "good"对应"676f6f64", "morning"对应"6d6f726e696e67"
template <typename T, typename... Args> inline decltype(auto) CallContract(const Identity& contract_id/*in*/, const std::string& method/*in*/, uint64_t value/*in*/, uint64_t gas/*in*/, Args... args);
调用另一个合约。
这是Contract类的成员方法,只有继承了Contract的合约才能调用该方法。
请求参数:
参数 | 类型 | 说明 |
contract_id | const Identity& | 要调用的合约的Identity |
method | const std::string& | 被调用的合约方法名称 |
value | uint64_t | 给被调用合约转移的资产数量 |
gas | uint64_t | 给被调用合约的gas数量,填写0表示将本合约所有可用gas给被调用合约使用 |
args | 任意个不定类型数参数 | 调用合约传入的参数列表,取决于被调用合约方法接收几个参数。 |
返回值:
参数 | 类型 | 说明 |
返回值 |
| 返回结构体 |
code | int | - 0:子合约正常 - 1:子合约不存在或给子合约转账失败 - 10200:子合约gas不足 - 10201:子合约执行过程中出现异常 - 10622:子合约的字节码非法(可能原因子合约不是wasm合约) |
result | T | 如果code为0, 该值为被调用方法的返回值;否则该值无意义。若T为void,则没有该字段 |
msg | std::string | - 若code为0,该值无意义 - 若code为10201,该值表示被调用合约的抛出的异常信息 |
int DelegateCall(const Identity& contract_id/*in*/, const std::string& method/*in*/, ...);
代理调用另一个合约。
请求参数:
参数 | 类型 | 说明 |
contract_id | const Identity& | 要调用的合约 ID |
method | const std::string& | 被调用的合约方法名 |
args | 自定义的不定类型参数数量 | 调用合约传入的参数列表,取决于被调用合约方法接收几个参数。 |
返回值:
参数 | 类型 | 说明 |
result | int | 正常调用返回 0,否则返回 1。 |
uint64_t GetGas();
获取本合约当前剩余的可用gas数量。如果是交易调用某个合约,则 gas 值就是交易中指定的 gas。如果是合约调用另一个合约,则 gas 值是 CallContract()
方法中指定的 gas。如果是合约代理调用另一个合约,那么 gas 值与父合约中的GetGas()
值相同。
返回值:
参数 | 类型 | 说明 |
result | uint64_t | 本合约当前剩余的可用gas数量 |
uint64_t GetValue();
获取本次合约调用本合约接收的资产数量。如果是交易调用合约,value
的值就是交易中指定的 value
值;如果是合约调用另一个合约,value
的值就是 CallContract()
方法中指定的 value
值。
返回值:
参数 | 类型 | 说明 |
result | uint64_t | 获取的 value |
std::string GetData();
获取触发交易的 data
字段值(本次合约调用传入的参数)。
返回值:
参数 | 类型 | 说明 |
result | std::string | 交易调用本合约时,传递的inputdata内容,原始字节串,非16进制 |
Identity GetSender();
返回调用者的 ID(账号或合约),合约执行过程中,消耗调用方(sender)的 gas。
返回值:
参数 | 类型 | 说明 |
result | Identity | 调用者 ID,参见 合约运行的上下文环境。 |
Identity GetSelf();
获取本合约的 ID。
返回值:
参数 | 类型 | 说明 |
result | Identity | 本合约的 ID |
uint32_t GetRelatedTransactionListSize(const Identity& recipient_id,uint64_t deposit_flag,uint64_t& count);
获取指定账户、指定flag的关联存证交易数量。
请求参数:
参数 | 类型 | 说明 |
recipient_id | const Identity& | 要查询的账户,即在关联存证交易中的to账户 |
deposit_flag | uint64_t | 指定的关联存证的flag |
count | uint64_t& | 查到的结果数量 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10350: 失败,交易不存在 |
uint32_t GetRelatedTransactionList(const Identity& recipient_id,uint64_t deposit_flag,uint64_t start_indexuint32_t number,std::vector<std::string>& tx_list);
查询指定账户、指定flag的关联存证交易列表。
请求参数:
参数 | 类型 | 说明 |
recipient_id | const Identity& | 要查询的账户,即在关联存证交易中的to账户 |
deposit_flag | uint64_t | 指定的关联存证的flag |
start_index | uint64_t | 指定开始索引 |
number | uint32_t | 指定个数 |
tx_list |
| 获取到的结果 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10350: 失败,start_index或number错误 |
uint32_t GetTransactionSender(const std::string& tx_hash, Identity& sender);
查询指定交易的发送者。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
sender | Identity& | 该交易的发送者 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetTransactionReceiver(const std::string& tx_hash Identity& receiver);
查询指定交易的接收者。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
receiver | Identity& | 该交易的接收者 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetTransactionTimestamp(const std::string& tx_hash, uint64_t& timestamp);
查询指定交易的上链时间。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
timestamp | uint64_t& | 该交易的上链时间 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetTransactionData(const std::string& tx_hash, std::string& data);
查询指定交易存证数据。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
data | std::string& | 该交易的存证数据,其内容是关联性标识和用户数据的组合内容 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetTransactionBlockIndex(const std::string& tx_hash, uint64_t& block_number, uint32_t& index);
查询指定交易的所在区块和在区块中的顺序。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
block_number | uint64_t& | 该交易所在区块号 |
index | uint32_t& | 该交易在区块中的顺序 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10310: 失败,交易不存在 |
uint32_t GetTransactionDepositFlag(const std::string& tx_hash, uint64_t& deposit_flag);
查询指定交易的关联性标识。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
deposit_flag | uint64_t& | 该交易的关联性标识 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetConfidentialDepositData(const std::string& tx_hash, std::string& data);
查询指定交易机密存证数据。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
data | std::string& | 该交易的存证数据,其内容是关联性标识和用户数据的组合内容 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
uint32_t GetConfidentialDepositFlag(const std::string& tx_hash, uint64_t& deposit_flag);
查询指定交易机密存证的关联性标识。
请求参数:
参数 | 类型 | 说明 |
tx_hash | const std::string& | 要查询的交易hash,长度32字节,只支持原始格式,不支持16进制格式 |
deposit_flag | uint64_t& | 该交易的关联性标识 |
返回值:
参数 | 类型 | 说明 |
result | uint32_t | - 0: 成功 - 10315: 失败,交易所在区块超出了查询限制 - 10316: 失败,交易类型不是存证交易 - 10310: 失败,交易不存在 |
int CreateContract(const Identity& src, const Identity& new_id);
根据合约模版创建新合约。
请求参数:
参数 | 类型 | 说明 |
src | const Identity& | 合约模版地址 |
new_id | const Identity& | 所创建的新合约地址 |
返回值:
参数 | 类型 | 说明 |
result | int | 创建成功返回0,否则返回1 如果创建成功,则新合约: - ID:传入的new_id - Balance:0 - AuthMap:和合约模版一致 - RecoverKey:和合约模版一致 - EncryptionKey:和合约模版一致 - key,value对为空 |
bool IsLocalTx();
判断当前交易的执行环境是否是local执行。
请求参数:无
返回值:
参数 | 类型 | 说明 |
result | bool | 表示当前交易执行环境是否local环境 |
bool GetTargetAddress(std::string targetName, Identity& id);
采用平台特定的hash算法计算 targetName字符串的hash值, 平台的hash算法在创世块中配置, 可以是: sha256, SM3, KECCAK256...
请求参数:
参数 | 类型 | 说明 |
targetName | std::string | hash计算的目标字符串 |
id | Identity& | hash计算结果 |
返回值:
参数 | 类型 | 说明 |
result | bool | targetName hash计算是否成功, 失败返回 false, 此时 id的内容不变, 成功返回 true, id值有效 |
工具类 API
template <class T> std::string pack(T t);
将 t
序列化为 std::string。
请求参数:
参数 | 类型 | 说明 |
t | T | 要序列化的数据,支持的数据类型见 可序列化数据类型。 |
返回值:
参数 | 类型 | 说明 |
result | std::string | 序列化结果 |
template <typename T> T unpack(const std::string& b);
将 std::string 反序列化为指定类型的数据。
请求参数:
参数 | 类型 | 说明 |
b | std::string | 要解析的数据内容 |
返回值:
参数 | 类型 | 说明 |
result | T | 解析结果,支持的数据类型见 可序列化数据类型。 |
pack
和 unpack
使用示例:
```C++
std::string buff = pack(“hello”s);std::string str = unpack
(buff);//str的值为hello
std::string buff = pack(1234);int n = unpack
(buff);//n的值为1234
```
bool Digest(const std::string& data/*in*/, DigestType type/*in*/, std::string& output/*out*/);
返回 data
的 SHA256 散列值。
请求参数:
参数 | 类型 | 说明 |
data | const std::string& | 要 hash 的数据 |
type | DigestType | 散列算法,支持的散列算法: SHA256,SM3 |
output | std::string& | 对data进行散列的结果,原始字节串,非16进制格式 |
返回值:
参数 | 类型 | 说明 |
result | bool | 如果输入不支持的type类型,则返回false,如果计算成功则返回true |
bool VerifyRsa(const std::string &pk/*in*/, const std::string &sig/*in*/, const std::string &msg/*in*/, DigestType hash_type=SHA256);
验证一个 RSA 签名是否有效。
请求参数:
参数 | 类型 | 说明 |
pk | const std::string& | DER格式的公钥 |
sig | const std::string& | 签名,必须是PKCS#1编码的格式 |
msg | const std::string& | 源消息 |
hash_type | DigestType | 散列算法。默认为SHA256,可选值:SHA256, SHA1 |
返回值:
参数 | 类型 | 说明 |
result | bool | 如果验证通过返回 true,否则返回 false。 |
std::string Hex2Bin(const std::string& input/*in*/);
Hex字符串转字节串std::string。
请求参数:
参数 | 类型 | 说明 |
input | const std::string& | 16 进制字节串 |
返回值:
参数 | 类型 | 说明 |
result | std::string | 若输入的 |
std::string Bin2Hex(const std::string& input/*in*/, bool uppercase=false/*in*/);
字节串转 16 进制字符串。
请求参数:
参数 | 类型 | 说明 |
input | const std::string& | 要转换的字节串。 |
uppercase | bool | 是否是大写,默认 false。 |
返回值:
参数 | 类型 | 说明 |
result | std::string | 转换结果 |
bool Base64Encode(const std::string& input/*in*/, std::string& output/*out*/);
base64编码。
请求参数:
参数 | 类型 | 说明 |
input | const std::string& | 进行base64编码的原数据,不允许为空,若为空则返回false。 |
output | std::string | base64编码结果。若返回值为false,output不会被修改。 |
返回值:
参数 | 类型 | 说明 |
result | bool | 如果编码成功返回true,否则返回false。 |
bool Base64Decode(const std::string& input/*in*/, std::string& output/*out*/);
base64解码。
请求参数:
参数 | 类型 | 说明 |
input | const std::string& | base64编码的字符串 |
output | std::string | 对input进行base64解码的结果,若返回值为false,output不会被修改 |
返回值:
参数 | 类型 | 说明 |
result | bool | 如果input是非法的base64字符串,则返回false,否则返回true |
bool Ecrecovery(const std::string& hash/*in*/, const std::string signature/*in*/, Identity& id/*out*/);
根据 hash 和签名恢复 ID。
请求参数:
参数 | 类型 | 说明 |
hash | const std::string& | 消息的hash |
signature | std::string | 消息的签名 |
id | Identity& | 要恢复的 ID,如果返回值为false,则Identity不会被修改 |
返回值:
参数 | 类型 | 说明 |
result | bool | 如果 hash 和 signature 非法或不匹配,则返回false,恢复失败。如果恢复成功则返回true。 |
bool VerifyMessageSM2(const std::string& pk, const std::string& sig, const std::string& msg);
验证SM2国密签名。
请求参数:
参数 | 类型 | 说明 |
pk | const std::string& | asn1 ECC 公钥 |
sig | const std::string& | (r, s, v) (65 bytes) 或 ASN.1 编码的格式 (<=72 字节) |
msg | const std::string& | 原始消息 |
返回值:
参数 | 类型 | 说明 |
result | bool | - 如果验证签名成功,返回true; - 如果验证签名失败,返回false。 |
bool VerifyMessageECCK1(const std::string& pubkey, const std::string& sig, const std::string& msg, DigestType hash_type);
验证ECC椭圆曲线K1产生的签名。
请求参数:
参数 | 类型 | 说明 |
pubkey | const std::string& | asn1 ECC pubkey格式或 65 字节长的压缩格式 |
sig | const std::string& | (r, s, v) (65 bytes) 或 ASN.1 编码的格式 (<=72 字节) |
msg | const std::string& | 原始消息 |
hash_type | DigestType | 散列的算法,支持的值: - DigestType::SHA256 - DigestType::SHA1 |
返回值:
参数 | 类型 | 说明 |
result | bool | - 如果验证签名成功,返回true; - 如果验证签名失败,返回false。 |
bool VerifyMessageECCR1(const std::string& pubkey, const std::string& sig, const std::string& msg, DigestType hash_type);
验证ECC椭圆曲线R1产生的签名。
请求参数:
参数 | 类型 | 说明 |
pubkey | const std::string& | asn1 ECC pubkey格式或 65 字节长的压缩格式 |
sig | const std::string& | (r, s, v) (65 bytes) 或 ASN.1 编码的格式 (<=72 字节) |
msg | const std::string& | 原始消息 |
hash_type | DigestType | 散列的算法,支持的值: - DigestType::SHA256 - DigestType::SHA1 |
返回值:
参数 | 类型 | 说明 |
result | bool | - 如果验证签名成功,返回true; - 如果验证签名失败,返回false。 |
int BellmanSnarkVerify(const std::string& verification_key, const std::vector<std::string>& inputs, const std::string& proof);
验证zksnarks的proof是否合法。
请求参数:
参数 | 类型 | 说明 |
verification_key | const std::string& | setup阶段产生的验证key |
inputs |
| 公开输入 |
proof | const std::string& | 零知识证明的证据 |
返回值:
参数 | 类型 | 说明 |
result | int | - 如果验证proof通过,返回0; - 如果验证签名失败,返回对应的错误码。 |
Pedersen密码学API
以下Pedersen密码学API仅在非TEE版本中支持。
int32_t RangeProofVerify(const std::string& proof, const std::vector<PC>& pc_list);
Pedersen范围验证。
请求参数:
参数 | 类型 | 说明 |
proof | const std::string& | 证据 |
pc_list |
| pedersen commitment |
返回值:
参数 | 类型 | 说明 |
result | int32_t | - - - |
int AddPedersenCommit(PC& PC_result, const PC& PC_left, const PC& PC_right);
PC相加,PC即std::string。
请求参数:
参数 | 类型 | 说明 |
PC_result | std::string& | PC相加结果 |
PC_left |
| 相加的左PC |
PC_right |
| 相加的右PC |
返回值:
参数 | 类型 | 说明 |
result | int32_t | - - |
int SubPedersenCommit(PC& PC_result, const PC& PC_left, const PC& PC_right);
PC相减,PC即std::string。
请求参数:
参数 | 类型 | 说明 |
PC_result | std::string& | PC相减结果 |
PC_left |
| 左PC |
PC_right |
| 右PC |
返回值:
参数 | 类型 | 说明 |
result | int32_t | - - |
int32_t CalculatePedersenCommit(PC& dst_pc, const PC& src_pc, const std::vector<PC>& positive, const std::vector<PC>& negative);
PC计算。
请求参数:
参数 | 类型 | 说明 |
dst_pc | PC& | PC结果 |
src_pc | const PC& | PC初值 |
positive |
| 正PC列表 |
negative |
| 负PC列表 |
返回值:
参数 | 类型 | 说明 |
result | int32_t | - - |
int PedersenCommitEqualityVerify(const std::vector<PC>& positive, const std::vector<PC>& negative);
PC等式验证。
请求参数:
参数 | 类型 | 说明 |
positive |
| 正PC列表 |
negative |
| 负PC列表 |
返回值:
参数 | 类型 | 说明 |
result | int32_t | - - - |
ElGamal隐私保护API(半同态加密)
以下ElGamal隐私保护API采用半同态加密技术,并且仅在非TEE版本中支持。
int LiftedElgamalContractHomomorphicAdd( const std::string& first, const std::string& second, std::string& res);
计算两个密文相加。
请求参数:
参数 | 类型 | 说明 |
first | const std::string& | 第一个密文原始字节串,长度要求为68字节,否则返回 |
second | const std::string& | 第二个密文原始字节串,长度要求为68字节,否则返回 |
res | std::string& | 两个密文相加结果,长度为68字节 |
返回值:
参数 | 类型 | 说明 |
result | int | - 1:执行成功; - 0:执行失败,非法的密文; - 2000:参数格式错误。 |
int LiftedElgamalContractHomomorphicSub(const std::string& first, const std::string& second, std::string& res);
计算两个密文相减。
请求参数:
参数 | 类型 | 说明 |
first | const std::string& | 第一个密文原始字节串,长度要求为68字节,否则返回 |
second | const std::string& | 第二个密文原始字节串,长度要求为68字节,否则返回 |
res | std::string& | 两个密文相减结果,长度为68字节 |
返回值:
参数 | 类型 | 说明 |
result | int | - 1:执行成功; - 0:执行失败,非法的密文; - 2000:参数格式错误。 |
int LiftedElgamalScalarMultiply(const std::string& src, uint64_t scalar, std::string& res);
同态相乘。
请求参数:
参数 | 类型 | 说明 |
src | const std::string& | 乘法密文原始字节串,长度要求为68字节,否则返回 |
scalar | uint64_t | 乘法标量 |
res | std::string& | 乘法结果,长度为68字节 |
返回值:
参数 | 类型 | 说明 |
result | int | - 1:执行成功; - 0:执行失败,非法的密文; - 2000:参数格式错误。 |
int LiftedElgamalContractZeroCheckVerify(const std::string& ciphertext, const std::string& proof, int& verify_result);
零值检测。
请求参数:
参数 | 类型 | 说明 |
ciphertext | const std::string& | 单个Elgamal密文原始字节串,长度要求68,否则返回 |
proof | const std::string& | 证据 |
verify_result | int& | 检测结果: - 1: 检测通过 - 0: 检测不通过 |
返回值:
参数 | 类型 | 说明 |
result | int | 若返回结果不为1,则verify_result的值没有意义。 - 1:函数执行成功; - 0:函数执行失败; - 10001:参数格式错误。 |
int LiftedElgamalContractRangeVerify(const std::vector<std::string>& ciphertext, const int per_value_bit_size, const std::string& public_key, const std::string& proof, int& verify_result);
范围检测。
请求参数:
参数 | 类型 | 说明 |
ciphertext |
| 密文列表,每个密文要求68字节,否则报错 |
per_value_bit_size | int | 验证范围证明时,证明验证的范围 |
public_key | const std::string& | 公钥,长度33字节 |
proof | const std::string& | 范围证明的证据 |
verify_result | int& | 检测结果: - 1: 检测通过 - 0: 检测不通过 |
返回值:
参数 | 类型 | 说明 |
result | int | 若返回结果不为1,则verify_result的值没有意义。 - 1:函数执行成功; - 0:函数执行失败; - 10001:参数格式错误。 |
调试类 API
int print(const char* format, ...);
打印输出函数。
请求参数:
参数 | 类型 | 说明 |
format | const char* | 格式与 C99 中 |
返回值:
参数 | 类型 | 说明 |
result | int | 恒为 0 |
内置类库
时间格式化
wasm 合约内无法直接获取系统时间信息,因为区块链不同节点的时间无法保证一致。用户可以通过GetBlockTimeStamp
,或者合约调用传参的方式,在合约内获取一个有一致性保证的时间信息。
MYCDT 提供了标准的 C/C++ 时间格式化接口,可以把此类时间信息按照用户需求格式化成一个字符串。
接口定义
localtime
struct tm *localtime (const time_t * t); struct tm *localtime64 (const time64_t * t);
把以秒为单位的时间t分解后转换存入struct tm格式。该接口默认时区是CST北京时区。
返回值
参数
分解后的时间信息结构体tm,后续时间格式化函数strftime需要。返回的tm中会被隐含初始化为CST北京时区。
分解后的时间信息结构体tm,后续时间格式化函数strftime需要。返回的tm中会被隐含初始化为CST北京时区。
strftime
size_t strftime (char *__restrict, size_t, const char *__restrict, const struct tm *__restrict);
根据指定的格式符,格式化tm格式的时间,并把结果放入指定的buffer。
返回值
参数
恒为0
参数1:存放格式后结果字符串的buffer指针
参数2:buffer长度
参数3:格式化格式符组合
参数4:struct tm格式的时间信息
gmtime
struct tm *gmtime (const time_t *); struct tm *gmtime64 (const time64_t *);
把以秒为单位的时间t分解后转换存入struct tm格式。该接口默认时区是GMT。
返回值
参数
分解后的时间信息结构体tm,后续时间格式化函数strftime需要。返回的tm中会被隐含初始化为GMT时区。
以秒为单位的时间,如果原始时间戳是以毫秒为单位,需要除以1000,转换成秒后再传入。
默认的time_t
类型内部使用 32 位整数来表示时间,因此无法表示 2038 年以后的时间。使用time64_t
类型和配套的接口可以避免这个问题。
示例一
#include <mychainlib/contract.h>
#include <locale_impl.h>
#include <time.h>
#include <locale.h>
using namespace mychain;
class hello:public Contract {
public:
INTERFACE void hi(uint64_t x ) {
//长度26,此处是示例值。请设置为实际格式化后字符串的长度。
//此处指定长度可以大于实际长度
//如果小于实际长度,则会发生截断
char buffer[26];
struct tm *pt;
//此处假定1585018706922是调用 GetBlockTimeStamp() 获得的毫秒时间戳
//其长度是64位整型
//因此需要先除以1000转换为秒
time_t mytime = (time_t)(1585018706922 / 1000);
//CST: 20200324105826
pt = localtime(&mytime);
strftime(buffer, 26, "%Y%m%d%H%M%S", pt);
println("CST: %s\n", buffer);
//Full: Tue Mar 24 10:58:26 2020
strftime(buffer, 26, "%c", pt);
println("Full: %s\n", buffer);
//GMT: 20200324025826
pt = gmtime(&mytime);
strftime(buffer, 26, "%Y%m%d%H%M%S", pt);
println("GMT: %s\n", buffer);
}
};
INTERFACE_EXPORT(hello, (hi))
示例二
以下给出使用std::put_time
接口来格式化时间戳的例子。
由于该接口引入了stringstream
,可能会导致合约体积显著增大。优点是不用显式的管理格式化后的字符串buffer了。
#include <mychainlib/contract.h>
#include<iomanip>
#include<ctime>
#include<sstream>
#include<string>
using namespace mychain;
class hello:public Contract {
public:
INTERFACE void hi(uint64_t x ) {
//此处假定1585018706922是调用 GetBlockTimeStamp() 获得的毫秒时间戳
//其长度是64位整型
//因此需要先除以1000转换为秒
std::time_t mytime = (std::time_t)(1585018706922 / 1000);
//也可以调用gmtime接口格式化为0时区时间
std::tm tm = *std::localtime(&mytime);
std::stringstream ssTp;
ssTp << std::put_time(&tm, "%Y%m%d%H%M%S");
//ssTp.str() 返回一个string对象
println("PUT_TIME: %s\n", ssTp.str().c_str());
}
};
INTERFACE_EXPORT(hello, (hi))
示例三
下面使用time64_t
类型来表示 2038 年以后的时间。
#include <mychainlib/contract.h>
#include <ctime>
#include <iomanip>
#include <sstream>
class Hello {
public:
INTERFACE void hi() {
time64_t t = 2234491342; // 某个 2040 年的时间
std::ostringstream oss;
oss << std::put_time(localtime64(&t), "%c");
mychain::println("%s", oss.str().c_str());
// 输出:Mon Oct 22 12:02:22 2040
oss.str("");
oss << std::put_time(gmtime64(&t), "%c");
mychain::println("%s", oss.str().c_str());
// 输出:Mon Oct 22 04:02:22 2040
}
};
INTERFACE_EXPORT(Hello, (hi))
第三方库
XML 库版本 v1.1.0,需要包含头文件。
#include <third_party/rapidxml/rapidxml.hpp> #include <third_party/rapidxml/rapidxml_print.hpp>
使用示例:
#include <mychainlib/contract.h> #include <third_party/rapidxml/rapidxml.hpp> #include <third_party/rapidxml/rapidxml_print.hpp> using namespace mychain; // NOLINT class TestXMLParse : public Contract { public: INTERFACE XMLParse() { std::string for_parse = R"(<?xml version="1.0" encoding="UTF-8"?> <EllipsoidParams> <Datum Name="BeiJing54" SemiMajorAxis="6378245.0" Flattening="298.3"/> <Datum Name="XiAn80" SemiMajorAxis="6378140.0" Flattening="298.257"/> <Datum Name="CGCS2000" SemiMajorAxis="6378137.0" Flattening="298.257222101"/> </EllipsoidParams>)"; // need memory more than 64*1024, so the stack can't hold it, use "static" to // make it allocate in global static rapidxml::xml_document<> doc; doc.parse<0>(for_parse.data()); const rapidxml::xml_node<>* ellipsoid = doc.first_node("EllipsoidParams"); if (NULL != ellipsoid) { for (rapidxml::xml_node<>* datum = ellipsoid->first_node("Datum"); NULL != datum; datum = datum->next_sibling()) { std::string szTmp; for (rapidxml::xml_attribute<char>* attr = datum->first_attribute("Name"); attr != NULL; attr = attr->next_attribute()) { szTmp.append(attr->name()); szTmp.append(": "); szTmp.append(attr->value()); szTmp.append(", "); } print("%s\n", szTmp.c_str()); } } } }; INTERFACE_EXPORT(TestXMLParse, (XMLParse))
JSON 库版本 v1.1.0
智能合约平台目前支持 rapidjson 和 jsoncpp 两种JSON 解析库,您可以根据自己的需要选择使用。下面展示两种 JSON 解析库的简单用法,您也可以到官方网站查询更详细的使用方法。
rapidjson 使用示例
#include <mychainlib/contract.h> #include <third_party/rapidjson/document.h> #include <third_party/rapidjson/stringbuffer.h> #include <third_party/rapidjson/writer.h> using namespace mychain; // NOLINT class TestJsonParse : public Contract { public: INTERFACE JsonParse() { using namespace ::rapidjson; std::string stringFromStream = R"({ "dictVersion": 1, "content": [ {"key": "word1", "value": "单词1"} , {"key": "word2", "value": "单词2"} , {"key": "word3", "value": "单词3"} , {"key": "word4", "value": "单词4"} , {"key": "word5", "value": "单词5"} ] })"; // ---------------------------- read json -------------------- // parse json from string. using rapidjson::Document; Document doc; doc.Parse<0>(stringFromStream.c_str()); if (doc.HasParseError()) { rapidjson::ParseErrorCode code = doc.GetParseError(); MyAssert(code == kParseErrorNone, "parse from string"); } // use values in parse result. using rapidjson::Value; Value& v = doc["dictVersion"]; if (v.IsInt()) { print("%d\n", v.GetInt()); } Value& contents = doc["content"]; if (contents.IsArray()) { for (size_t i = 0; i < contents.Size(); ++i) { Value& v = contents[i]; assert(v.IsObject()); if (v.HasMember("key") && v["key"].IsString()) { print("%s\n", v["key"].GetString()); } if (v.HasMember("value") && v["value"].IsString()) { print("%s\n", v["value"].GetString()); } } } // ---------------------------- write json -------------------- print("add a value into array\n"); Value item(Type::kObjectType); item.AddMember("key", "word5", doc.GetAllocator()); item.AddMember("value", "单词5", doc.GetAllocator()); contents.PushBack(item, doc.GetAllocator()); // convert dom to string. StringBuffer buffer; // in rapidjson/stringbuffer.h Writer<StringBuffer> writer(buffer); // in rapidjson/writer.h doc.Accept(writer); print("%s\n", buffer.GetString()); } }; INTERFACE_EXPORT(TestJsonParse, (JsonParse))
jsoncpp 使用示例
#include <mychainlib/contract.h> #include <third_party/jsoncpp/json.h> using namespace mychain; class TestJsoncpp : public Contract { public: INTERFACE void parse() { std::string strValue = "{ \ \"key\": \"value1\", \ \"array\": [ \ {\"arraykey\": 1}, \ {\"arraykey\": 2} \ ] \ }"; Json::Reader reader; Json::Value root; if (reader.parse(strValue, root)) { if (!root["key"].isNull()) { std::string strValue= root["key"].asString(); println("%s\n", strValue.c_str()); } Json::Value arrayObj = root["array"]; for (int i=0; i<arrayObj.size(); i++) { int iarrayValue = arrayObj[i]["arraykey"].asInt(); println("%d\n", iarrayValue); } } } }; INTERFACE_EXPORT(TestJsoncpp, (parse))
定点数库,需要包含头文件
#include <third_party/bcmath/bcmath_stl.h>
使用示例
#include <mychainlib/contract.h> #include <third_party/bcmath/bcmath_stl.h> using namespace mychain; using namespace bcmath; void test_bcmath(); class BCMathTest: public Contract { public: INTERFACE void run() { test_bcmath(); } }; INTERFACE_EXPORT(BCMathTest, (run)) void test_bcmath() { BCMath::bcscale(4); //Num Decimals BCMath test("-5978"); std::string gold; auto require = [](bool cond, const char* msg) { Require(cond, "Test failed: "s + msg); }; test^=30; //Pow, only integers. Not work decimals. gold = "198005530669253749533290222782634796336450786581284861381777714804795900171726938603997395193921984842256586113024"; require(test.toString() == gold, "BigDecimal 1"); test-=1.23; //sub gold = "198005530669253749533290222782634796336450786581284861381777714804795900171726938603997395193921984842256586113022.7700"; require(test.toString() == gold, "BigDecimal 2"); test*=1.23; //mul gold = "243546802723182111925946974022640799493834467494980379499586589209898957211224134482916796088524041355975600919018.0071"; require(test.toString() == gold, "BigDecimal 3"); test*=-1.23; //mul gold = "-299562567349513997668914778047848183377416395018825866784491504728175717369805685413987659188884570867849989130392.1487"; require(test.toString() == gold, "BigDecimal 4"); BCMath::bcscale(70); //Num Decimals BCMath randNum("-5943534512345234545.8998928392839247844353457"); BCMath pi("3.1415926535897932384626433832795028841971693993751058209749445923078164062862"); BCMath result1 = randNum + pi; BCMath result2 = randNum - pi; BCMath result3 = randNum * pi; BCMath result4 = randNum / pi; gold = "-5943534512345234542.7583001856941315459727023167204971158028306006248941790250554076921835"; require(result1.toString() == gold, "Super Precision 1"); gold = "-5943534512345234549.0414854928737180228979890832795028841971693993751058209749445923078164"; require(result2.toString() == gold, "Super Precision 2"); gold = "-18672164360341183116.9114783895073349180904753962992796943871920962352436079118338887287186"; require(result3.toString() == gold, "Super Precision 3"); gold = "-1891885794154043400.2804849527556211973567525043250278948318788149660700494315139982452600"; require(result4.toString() == gold, "Super Precision 4"); //Other example BCMath::bcscale(4); //Num Decimals gold = "8023400.1075"; require(BCMath::bcmul("1000000.0134", "8.0234") == gold, "Other 1"); gold = "1000008.0368"; require(BCMath::bcadd("1000000.0134", "8.0234") == gold, "Other 2"); require(BCMath::bccomp("1", "2") == -1, "Compare 1"); require(BCMath::bccomp("1.00001", "1", 3) == 0, "Compare 2"); require(BCMath::bccomp("1.00001", "1", 5) == 1, "Compare 3"); require(BCMath("1")< BCMath("2"), "Compare 4"); require(BCMath("1")<=BCMath("2"), "Compare 5"); require(!(BCMath("1")> BCMath("2")), "Compare 6"); require(!(BCMath("1")>=BCMath("2")), "Compare 7"); require(!(BCMath("2")< BCMath("2")), "Compare 8"); require(BCMath("2")<=BCMath("2"), "Compare 9"); require(!(BCMath("2")> BCMath("2")), "Compare 10"); require(BCMath("2")>=BCMath("2"), "Compare 11"); gold = "123.0125"; require(BCMath::bcround("123.01254") == gold, "Round 1"); gold = "-123.013"; require(BCMath::bcround("-123.01254", 3) == gold, "Round 2"); gold = "123.01"; require(BCMath::bcround("123.01254", 2) == gold, "Round 3"); pi.round(3); gold = "3.142"; require(pi.toString() == gold, "Round 4"); BCMath part1("-.123"); BCMath part2(".123"); BCMath part3("123"); require(part1.getIntPart() == "-0", "Int part 1"); require(part1.getDecPart() == "123", "Dec part 1"); require(part2.getIntPart() == "0", "Int part 2"); require(part2.getDecPart() == "123", "Dec part 2"); require(part3.getIntPart() == "123", "Int part 3"); require(part3.getDecPart() == "0", "Dec part 3"); }