解析basic智能合约(go):
basic合约是我们之前在调用示例合约的时候调用的合约,之前分析过java语言编写的,再分析一下go语言编写的。
fabric官方提供了源码,在fabric/scripts/fabric-samples/asset-transfer-basic/chaincode-go目录
先分析一下这个目录结构
chaincode-go
├── assetTransfer.go(应用程序的主要入口文件或资产转移的核心逻辑实现文件。)
├── chaincode (包含与区块链智能合约相关的文件。)
│ ├── mocks (包含一些用于测试目的的模拟文件或桩文件。)
│ │ ├── chaincodestub.go (用于模拟链码(chaincode)的存根(stub)文件。)
│ │ ├── statequeryiterator.go (用于模拟状态查询迭代器的文件。)
│ │ └── transaction.go (模拟交易的文件。)
│ ├── smartcontract.go (区块链智能合约的实现文件。)
│ └── smartcontract_test.go (用于测试区块链智能合约的测试文件。)
├── go.mod (Go 项目的模块文件,其中定义了项目的依赖关系和版本信息。)
├── go.sum (包含项目依赖项的安全校验和的文件,用于验证下载的包的完整性。)
└── vendor(包含项目依赖项的目录,通常在使用特定版本管理工具时会将依赖项放在此目录中。)
然后再逐一看看内部的代码
assertTransfer.go
代码语言:javascript复制package main
import (
"log"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
)
func main() {
assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
if err != nil {
log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
}
if err := assetChaincode.Start(); err != nil {
log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
}
}
然后逐行分析:
package main
: 这是Go语言中的一个包声明,表明这个文件是一个可执行程序的入口文件。import (...)
: 这里导入了多个包,用于引入所需的依赖项。"log"
: 这是Go语言标准库中的一个包,用于记录日志信息。"github.com/hyperledger/fabric-contract-api-go/contractapi"
: 这是一个Hyperledger Fabric提供的用于编写智能合约的Go API的包。"github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
: 这是一个与Hyperledger Fabric示例中的资产转移基础应用程序相关的自定义包。
func main() { ... }
: 这是程序的入口函数,它是程序启动时第一个被执行的函数。assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
: 这行代码创建了一个基于contractapi
包的新的链码(chaincode)实例,并将其赋值给变量assetChaincode
。它使用chaincode.SmartContract{}
作为智能合约的实现。if err != nil { ... }
: 这是一个错误处理的条件语句,用于检查链码实例的创建过程中是否发生了错误。如果发生错误,将会输出错误日志并终止程序运行。if err := assetChaincode.Start(); err != nil { ... }
: 这是另一个错误处理的条件语句,用于检查链码实例的启动过程中是否发生了错误。如果发生错误,将会输出错误日志并终止程序运行。
在总体上,这段代码的功能是创建一个基于Hyperledger Fabric的区块链应用程序,并启动该应用程序的链码实例。它使用了Hyperledger Fabric提供的链码API和自定义的智能合约实现。如果创建或启动过程中出现错误,程序将输出相应的错误日志并终止运行。
再看一下smartcontract.go
代码语言:javascript复制package chaincode
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
// Asset describes basic details of what makes up a simple asset
// Insert struct field in alphabetic order => to achieve determinism across languages
// golang keeps the order when marshal to json but doesn't order automatically
type Asset struct {
AppraisedValue int `json:"AppraisedValue"`
Color string `json:"Color"`
ID string `json:"ID"`
Owner string `json:"Owner"`
Size int `json:"Size"`
}
// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
assets := []Asset{
{ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
{ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
{ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
{ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
{ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
{ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
}
for _, asset := range assets {
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
err = ctx.GetStub().PutState(asset.ID, assetJSON)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}
return nil
}
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
asset := Asset{
ID: id,
Color: color,
Size: size,
Owner: owner,
AppraisedValue: appraisedValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}
return &asset, nil
}
// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
// overwriting original asset with new asset
asset := Asset{
ID: id,
Color: color,
Size: size,
Owner: owner,
AppraisedValue: appraisedValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
return ctx.GetStub().DelState(id)
}
// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return assetJSON != nil, nil
}
// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return "", err
}
oldOwner := asset.Owner
asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return "", err
}
err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
}
// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace.
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}
return assets, nil
}