编写你的第一个链码

前面废话不多说了,链码是什么懂的都懂

资产转移链码
这个应用是一个基础的样例链码,这个链码可以产生一个账本(这个账本能创建资产,读,更新,删除资产,检查是否一个资产存在,转移资产)
为你的代码选个位置

就在home目录下面创建即可,并建立链码go文件(到时候把代码放进去)

// atcc is shorthand for asset transfer chaincode
   mkdir atcc && cd atcc
   go mod init atcc
   touch atcc.go

接下来是构建链码的几个模块函数
内务处理(housekeeping)
也就是给链码输入进一些必要的依赖
我们输入fabric合约api的包并且定义我们的samrtcontract
导入链码依赖包:

package main

import (
  "fmt"
  "log"
  "github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset(智能合约提供的为了管理资产的函数)
   type SmartContract struct {
    
    
   contractapi.Contract
   }

接下来,添加一个结构Asset来表示在账本上的简单的资产

// Asset describes basic details of what makes up a simple asset
   type Asset struct {
    
    
    ID             string `json:"ID"`
    Color          string `json:"color"`
    Size           int    `json:"size"`
    Owner          string `json:"owner"`
    AppraisedValue int    `json:"appraisedValue"`
   }

建立链码
InitLedger函数来让账本有些基础数据

// 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)
  }

删除函数

// 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函数来实现。

// 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
    }

最后还有两个函数不介绍了,在后面总和代码里面有

下面就是提供代码。如果要看看他是怎么样的,你可以看在fabric-samples文件夹里面的asset-transfer Go chaincode。如果你看assetTransfer.go文件,你能看出他包含package main并且输入在smartcontract.go里面定义的package chaincode,位置在fabric-samples/asset-transfer-basic/chaincode-go/chaincode/.

总和
最后我们需要添加main函数,这个叫 ContractChaincode.Start函数。下面是源码:

package main

import (
  "encoding/json"
  "fmt"
  "log"

  "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
   type Asset struct {
    
    
      ID             string `json:"ID"`
      Color          string `json:"color"`
      Size           int    `json:"size"`
      Owner          string `json:"owner"`
      AppraisedValue int    `json:"appraisedValue"`
    }

// 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.
   func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
    
    
    asset, err := s.ReadAsset(ctx, id)
    if err != nil {
    
    
      return err
    }

    asset.Owner = newOwner
    assetJSON, err := json.Marshal(asset)
    if err != nil {
    
    
      return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
  }

// 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
  }

  func main() {
    
    
    assetChaincode, err := contractapi.NewChaincode(&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)
    }
  }

为链码管理外部依赖
你的go 链码依赖于go包,这个不是标准包里面的一部分。这些源一定要在你的链码包里当被安装到一个peer节点上。如果你已经把你的链码作为一个模块,首要做的是就是搞个依赖:

go mod tidy
go mod vendor

这样就把外部依赖放入一个本地的vendor目录

我的步骤
我在home新建了atcc文件夹,把依赖放进去,打入

peer lifecycle chaincode package atcc.tar.gz --path ../atcc/ --lang golang --label atcc_1.0

产生压缩包,并把压缩包复制到test-network文件夹

搭建环境

xport CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${
    
    PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

在test-network目录下输入:

peer lifecycle chaincode install atcc.tar.gz

在这里插入图片描述
接下来什么调用什么都不用再说了,第一篇博客里面有,基本上能安装,其他都好说
而且其实这个代码就是chaincode-go里面的smartcontract

猜你喜欢

转载自blog.csdn.net/qq_45368275/article/details/109103043