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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章