Hyperledger Fabric学习笔记——贸易票据案例中的智能合约(Java版本)

智能合约定义业务对象的不同状态,并控制对象在这些不同状态之间转换的过程。

1、Contract类

@Contract(...)
@Default
public class CommercialPaperContract implements ContractInterface {...}

@Contract注释为提供关于合约额外的信息提供了可能,比如许可、作者等。@Default注释将这个合约类声明为默认的合约类,这种做法在由多个合约类的智能合约中很有用。

import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.Contact;
import org.hyperledger.fabric.contract.annotation.Contract;
import org.hyperledger.fabric.contract.annotation.Default;
import org.hyperledger.fabric.contract.annotation.Info;
import org.hyperledger.fabric.contract.annotation.License;
import org.hyperledger.fabric.contract.annotation.Transaction;

导入需要的类、注释、Context类。

通常一个文件中只有一个智能合约,因为不同的合约有不同的生命周期,所以最好将它们分开。然而有时,多智能合约能为应用程序提供语法的帮助,比如EuroBond,DollarBond,YenBond,但是本质上提供相同的函数。

2、Transaction定义

@Transaction
public CommercialPaper issue(CommercialPaperContext ctx,
                             String issuer,
                             String paperNumber,
                             String issueDateTime,
                             String maturityDateTime,
                             int faceValue) {...}

@Transaction注释标注了这个方法是transaction定义。当应用程序请求issue一个商业票据的时候,该方法会自动调用,通过相应的变量传递对应的值。transaction context总是transaction方法的第一个参数,默认的,context包括了许多与transaction逻辑相关的信息。下面显示了这个智能合约如何通过实现自己的createContext()方法扩展默认的transaction context,而不是用默认的实现。

@Override
public Context createContext(ChaincodeStub stub) {
     return new CommercialPaperContext(stub);
}

这个扩展的context在默认的基础上添加了一个自定义的属性paperList。

class CommercialPaperContext extends Context {
    public CommercialPaperContext(ChaincodeStub stub) {
        super(stub);
        this.paperList = new PaperList(this);
    }
    public PaperList paperList;
}

buy方法和redeem方法。

@Transaction
public CommercialPaper buy(CommercialPaperContext ctx,
                           String issuer,
                           String paperNumber,
                           String currentOwner,
                           String newOwner,
                           int price,
                           String purchaseDateTime) {...}
@Transaction
public CommercialPaper redeem(CommercialPaperContext ctx,
                              String issuer,
                              String paperNumber,
                              String redeemingOwner,
                              String redeemDateTime) {...}

3、transaction逻辑

issue方法:

@Transaction
public CommercialPaper issue(CommercialPaperContext ctx,
                              String issuer,
                              String paperNumber,
                              String issueDateTime,
                              String maturityDateTime,
                              int faceValue) {

    System.out.println(ctx);

    // 创建一个paper对象
    CommercialPaper paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime,
            faceValue,issuer,"");

    // 将paper设置为issued状态
    paper.setIssued();

    // 设置paper的拥有者,新发行的拥有者为issuer
    paper.setOwner(issuer);

    System.out.println(paper);
    // 将paper加入账本世界状态类似的paper的列表中
    ctx.paperList.addPaper(paper);

    // 返回paper
    return paper;
}

buy方法:

@Transaction
public CommercialPaper buy(CommercialPaperContext ctx,
                           String issuer,
                           String paperNumber,
                           String currentOwner,
                           String newOwner,
                           int price,
                           String purchaseDateTime) {

    // 通过下面代码从list中取回当前paper
    String paperKey = State.makeKey(new String[] { paperNumber });
    CommercialPaper paper = ctx.paperList.getPaper(paperKey);

    // 验证当前的owner
    if (!paper.getOwner().equals(currentOwner)) {
        throw new RuntimeException("Paper " + issuer + paperNumber + " is not owned by " + currentOwner);
    }

    // 首先buy方法将状态从issued转换为trading
    if (paper.isIssued()) {
        paper.setTrading();
    }

    // 检查paper是否已经赎回
    if (paper.isTrading()) {
        paper.setOwner(newOwner);
    } else {
        throw new RuntimeException(
                "Paper " + issuer + paperNumber + " is not trading. Current state = " + paper.getState());
    }

    // 更新paper
    ctx.paperList.updatePaper(paper);
    return paper;
}

redeem方法:

@Transaction
    public CommercialPaper redeem(CommercialPaperContext ctx, String issuer, String paperNumber, String redeemingOwner,
            String redeemDateTime) {

        String paperKey = CommercialPaper.makeKey(new String[] { paperNumber });

        CommercialPaper paper = ctx.paperList.getPaper(paperKey);

        // 验证paper是否被赎回
        if (paper.isRedeemed()) {
            throw new RuntimeException("Paper " + issuer + paperNumber + " already redeemed");
        }

        // 确定被赎回的人在进行赎回前是paper的owner
        if (paper.getOwner().equals(redeemingOwner)) {
            paper.setOwner(paper.getIssuer());
            paper.setRedeemed();
        } else {
            throw new RuntimeException("Redeeming owner does not own paper" + issuer + paperNumber);
        }

        ctx.paperList.updatePaper(paper);
        return paper;
    }

4、CommercialPaper类

CommercialPaper类继承自State类。State类是一个应用定义的类,它创建了一个通用的状态抽象。所有的状态都有一个对应表示的业务对象,一个复合键,可以序列化或者去序列化。State在有多个业务对象在账本上时使代码更加清晰。

CommercialPaper类中主要定义了私有数据和一些简单的接口。

5、PaperList类

PaperList类用来管理fabric状态数据库中的所有商业票据,和CommercialPaper类一样,这个类也继承自一个应用程序定义的,用于为状态列表建立一个通用的抽象的类——StateList。

账本中的每一个状态数据都由两个基本元素构成:

Key:key由createCompositeKey()使用固定的名字和状态的key形成。在PaperList对象形成时key的名称分配完成,state.getSplitKey()决定每个状态的唯一key。

Data:data是通过State.serialize()方法建立的商业票据状态的序列化形式。State类通过JSON进行序列化和反序例化。

我的理解是PaperList感觉像是逻辑上的一个结构,具体的实现还是交给账本数据库和fabric API。

你可能感兴趣的:(Hyperledger,Fabric)