阿里云首页

数据存证写入

数据分类

为维护数据的可读性,区块链预先配置了不同业务场景下对应的数据分类,作为所有用户的存证数据格式约定。上链数据必须满足规范要求。

前置条件

在阅读此 开发示例 前,请与您所在的区块链的 管理员 沟通确定已配置的数据分类,如果您是管理员,请参考 配置数据分类 一节完成数据分类的配置;管理员完成数据分类配置后,就可以下载并解压SDK包,完成本文各示例讲解的功能开发。

SDK包下载方式为,登录BaaS平台,在左侧导航栏选择 区块链列表,在列表中找到您希望访问的区块链,鼠标选中右侧 更多,并点击 下载签名证书下载SDK

notary_chain_download

您可以在client-sdk.zip包内的 schema.txt 文件中查看区块链的数据分类。一条区块链可以包含多个业务数据分类(Category),链上会为每个分类分配一个Category ID,分类配置完成后,用户可以通过识别数据的Category ID在链上检索和过滤存证数据。

开发示例

这里通过一个简单的schema.txt文件示例来演示如何构造、读取存证数据,请对照您自己的文件内容,将GuestInfo类型替换为您实际的数据分类。文件声明了一个名为GuestInfo的Category,包含三个成员,分别是姓名、生日和邮件地址,均为字符串类型,注释中说明了成员数据须满足的约束条件。

说明

请注意:实际开发时,请将GuestInfo替换为您本地schema.txt中实际定义的Category。

// schema.txt定义:商家顾客信息
GuestInfo {
    String userName;  // 姓名,要求非空,长度在20以内
    String birthday;  // 生日,要求非空
    String email;     // 邮件地址,要求非空,符合电子邮件格式
}

对照规范要求,在client代码中调用GuestInfoBuilder构造一条存证数据。

// client代码
// 构造存证数据
GuestInfoBuilder builder = new GuestInfoBuilder();
byte[] bizData = builder.buildUserName("Bob")
                        .buildBirthday("2000-01-01")
                        .buildEmail("bob@inc.com")
                        .build();

存证数据构造完成了,接下来构造上链的payload,这里用户需要了解一个枚举类——BizCategory。SDK根据schema.txt的约定,预先生成了这一枚举类型,帮助用户标识不同的Category。本例中的BizCategory如下所示。

// SDK的BizCategory声明
public class BizCategory {
    public static final int GuestInfo = 0x00010001;
    ...
}

接下来我们把存证数据传入payload,指定Category为BizCategory.GuestInfo,构造一个数据存证交易,并通过client发送出去,存证上链的过程就完成了。

// client代码
// 创建ContentOnlyNotary Transaction
TransactionDO tx = TransactionBuilder.getContentOnlyNotaryPayloadBuilder()
        .setContent(bizData)//设置需要存证的数据
        .setTimestamp(System.currentTimeMillis())//设置业务时间
        .setCategory(BizCategory.GuestInfo)//通过BizCategory设置业务分类
        .build();//build Transaction
// 发送Transaction
Response<TransactionDO> response = client.sendTransaction(tx);

存证数据上链后,需要等待一段时间才能成块,出块后就可以查询数据了。在读取数据时,用户也同样通过BizCategory.GuestInfo来识别数据对象类型,将存证数据转换回对象。获取块数据有两种模式,模式一是监听成块消息,处理新数据块;模式二是通过定时任务,每隔一段时间拉取新的数据块。详情可参见 最佳实践

下面展示几个完整的示例。

例1. 简单的存证数据写入

// 加载client配置文件
Properties p = new Properties();
p.load(new FileInputStream("sdk.properties"));
ClientConfig config = new ClientPropertyConfig(p);

// 使用指定client配置初始化client
Client client = new Client(config);

// 构造存证数据
GuestInfoBuilder builder = new GuestInfoBuilder();
byte[] bizData = builder.buildUserName("Bob")
                        .buildBirthday("2000-01-01")
                        .buildEmail("bob@inc.com")
                        .build();

// 创建ContentOnlyNotary Transaction
TransactionDO tx = TransactionBuilder.getContentOnlyNotaryPayloadBuilder()
        .setContent(bizData)//设置需要存证的数据
        .setTimestamp(System.currentTimeMillis())//设置业务时间
        .setCategory(BizCategory.GuestInfo)//设置业务分类
        .build();//build Transaction

// 发送Transaction
Response<TransactionDO> response = client.sendTransaction(tx);

if(response.isSuccess()){
  // 发送成功,业务系统保留Transaction Hash与业务数据关联
  return tx.getTxHashValue();
}else{
  // 发送失败,抛异常。业务系统应该重试,rpc调用失败时区块链的状态是未知的。
  throw new RuntimeException("写入区块链失败");
}

例2. 存证数据读取

// 获取当前最新区块header
final Response<BlockHeader> blockHeader = client.getLatestBlockHeader();
if(!blockHeader.isSuccess()) {
    LOGGER.error("Get block header fail: ", blockHeader.getErrorMsg());
    return;
}
// oldHeight = 当前本地区块高度;
long beginHeight = oldHeight + 1;
long finalHeight = blockHeader.getData().getHeight();
// 发现新块则开始拉取
while(beginHeight < finalHeight) {
    long endHeight = Math.min(beginHeight + FETCH_LIMIT - 1, finalHeight);
    Response<List<Block>> response = client.getBlocks((int)beginHeight, (int)endHeight);
    if (!response.isSuccess()) {
        LOGGER.error("Get response fail: ", response.getErrorMsg());
        return;
    }
    ConstructUtil constructUtil = new ConstructUtil();
    List<Block> blocks = response.getData();
    for (Block block : blocks) {
        // 获取区块的存证交易
        List<TransactionDO> transactions = block.getTransactions();
        for (TransactionDO transaction : transactions) {
            // 获取payload
            if (ContentOnlyNotaryPayloadDO.class.isInstance(transaction.getPayload())) {
                ContentOnlyNotaryPayloadDO contentOnlyNotaryPayloadDO =
                    ContentOnlyNotaryPayloadDO.class.cast(transaction.getPayload());
                // 判断payload的业务数据分类
                if (contentOnlyNotaryPayloadDO.getCategory() == BizCategory.GuestInfo) {
                    // 构造数据对象
                    GuestInfo renter = GuestInfo.class.cast(constructUtil.construct(
                        contentOnlyNotaryPayloadDO.getContent(), GuestInfo.class));
                    // 读取renter数据
                    System.out.println(renter.getUserName());
                }
            }
        }
        beginHeight = block.getHeader().getHeight() + 1;
    }
}

最后如果用户的系统要退出,建议在退出前调用shutdown,client会在shutdown中优雅的关闭线程池。

client.shutdown();

更多

蚂蚁区块链使用Transaction(存证交易模型)进行数据存证,以上示例使用了ContentOnlyNotaryPayload和EncryptShareNotaryPayload两种存证模型,业务系统还可以根据具体场景,使用其它类型的Transaction写数据上链,详情请参考 存证交易模型

首页 数据存证写入