• 【区块链技术与应用】(五)


    在这里插入图片描述

    引言

    这周恰逢期中,时间仓促,代码上有许多地方可以优化,但也只能留到之后的几次作业上了。
    阅读建议:参考和链码样例为写链码前用样例试手内容,与作业相关的内容是“资产管理”之后的代码。
    代码参考及学习资料在“参考”一栏中。
    请添加图片描述

    参考

    https://blog.csdn.net/zekdot/article/details/120397660?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1-120397660-blog-125920006.pc_relevant_3mothn_strategy_and_data_recovery&spm=1001.2101.3001.4242.2&utm_relevant_index=4

    https://blog.csdn.net/weixin_49422491/article/details/125380911

    https://blog.csdn.net/weixin_44676392/article/details/87938176

    https://blog.csdn.net/weixin_44676392/article/details/87938451

    https://blog.csdn.net/qq_41988893/article/details/119706443

    fabric提供了fabric-contract-api-gofabric-chaincode-go两个包来编写链码, 这里以fabric-contract-api-go为例进行链码编写

    链码样例

    1.声明合约

    package main
    
    import (
        "errors"
        "fmt"
    
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    type SimpleContract struct {
        contractapi.Contract
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.编写合约函数

    规则:
    1、第一个参数必须是*contractapi.TransactionContext类型
    2、函数最多返回两个值,第二个值必须是error类型

    // 添加数据
    func (sc *SimpleContract) Create(ctx contractapi.TransactionContextInterface, key string, value string) error {
        existing, err := ctx.GetStub().GetState(key)
    
        if err != nil {
            return errors.New("Unable to interact with world state")
        }
    
        if existing != nil {
            return fmt.Errorf("Cannot create world state pair with key %s. Already exists", key)
        }
    	
        err = ctx.GetStub().PutState(key, []byte(value))
    
        if err != nil {
            return errors.New("Unable to interact with world state")
        }
    
        return nil
    }
    
    // 读取数据
    func (sc *SimpleContract) Read(ctx contractapi.TransactionContextInterface, key string) (string, error) {
        existing, err := ctx.GetStub().GetState(key)
    
        if err != nil {
            return "", errors.New("Unable to interact with world state")
        }
    
        if existing == nil {
            return "", fmt.Errorf("Cannot read world state pair with key %s. Does not exist", key)
        }
    
        return string(existing), nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    3.创建并启动链码

    package main
    
    import (
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    func main() {
        simpleContract := new(SimpleContract)
    
        cc, err := contractapi.NewChaincode(simpleContract)
    
        if err != nil {
            panic(err.Error())
        }
    
        if err := cc.Start(); err != nil {
            panic(err.Error())
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    完整合约代码:

    package main
    
    import (
        "errors"
        "fmt"
    
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    type User struct {
    
    }
    type SimpleContract struct {
        contractapi.Contract
    }
    
    
    //用户定义Struct
    
    
    // 添加数据
    func (sc *SimpleContract) Create(ctx contractapi.TransactionContextInterface, key string, value string) error {
        existing, err := ctx.GetStub().GetState(key)
    
        if err != nil {
            return errors.New("Unable to interact with world state")
        }
    
        if existing != nil {
            return fmt.Errorf("Cannot create world state pair with key %s. Already exists", key)
        }
    	
        err = ctx.GetStub().PutState(key, []byte(value))
    
        if err != nil {
            return errors.New("Unable to interact with world state")
        }
    
        return nil
    }
    
    // 读取数据
    func (sc *SimpleContract) Read(ctx contractapi.TransactionContextInterface, key string) (string, error) {
        existing, err := ctx.GetStub().GetState(key)
    
        if err != nil {
            return "", errors.New("Unable to interact with world state")
        }
    
        if existing == nil {
            return "", fmt.Errorf("Cannot read world state pair with key %s. Does not exist", key)
        }
    
        return string(existing), nil
    }
    
    func main() {
        simpleContract := new(SimpleContract)
    
        cc, err := contractapi.NewChaincode(simpleContract)
    
        if err != nil {
            panic(err.Error())
        }
    
        if err := cc.Start(); err != nil {
            panic(err.Error())
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    一个简单的智能合约就编写完了。

    特别注意,这不是一个完整的链码程序,中间缺少了用户定义的struct.

    test-network网络测试

    启动fabric网络
    1. 进入fabric-sample的test-network目录

      $ cd fabric-samples/test-network
      
      • 1
    2. 运行./network.sh up 启动网络

    magpie@Goserver:~/fabric-samples01/test-network$ ./network.sh up
    Starting nodes with CLI timeout of '5' tries and CLI delay of '3' seconds and using database 'leveldb' with crypto from 'cryptogen'
    LOCAL_VERSION=2.4.6
    DOCKER_IMAGE_VERSION=2.4.6
    /home/magpie/fabric-samples01/test-network/../bin/cryptogen
    Generating certificates using cryptogen tool
    Creating Org1 Identities
    + cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output=organizations
    org1.example.com
    + res=0
    Creating Org2 Identities
    + cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output=organizations
    org2.example.com
    + res=0
    Creating Orderer Org Identities
    + cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output=organizations
    + res=0
    Generating CCP files for Org1 and Org2
    /home/magpie/fabric-samples01/test-network/../bin/configtxgen
    Generating Orderer Genesis block
    + configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block
    2022-10-16 06:15:53.999 UTC [common.tools.configtxgen] main -> INFO 001 Loading configuration
    2022-10-16 06:15:54.026 UTC [common.tools.configtxgen.localconfig] completeInitialization -> INFO 002 orderer type: etcdraft
    2022-10-16 06:15:54.026 UTC [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 Orderer.EtcdRaft.Options unset, setting to tick_interval:"500ms" election_tick:10 heartbeat_tick:1 max_inflight_blocks:5 snapshot_interval_size:16777216 
    2022-10-16 06:15:54.026 UTC [common.tools.configtxgen.localconfig] Load -> INFO 004 Loaded configuration: /home/magpie/fabric-samples01/test-network/configtx/configtx.yaml
    2022-10-16 06:15:54.028 UTC [common.tools.configtxgen] doOutputBlock -> INFO 005 Generating genesis block
    2022-10-16 06:15:54.028 UTC [common.tools.configtxgen] doOutputBlock -> INFO 006 Writing genesis block
    + res=0
    [+] Running 7/7
     ⠿ Volume "docker_orderer.example.com"     Created                            0.0s
     ⠿ Volume "docker_peer0.org1.example.com"  Created                            0.0s
     ⠿ Volume "docker_peer0.org2.example.com"  Created                            0.0s
     ⠿ Container peer0.org1.example.com        Started                            3.4s
     ⠿ Container peer0.org2.example.com        Started                            1.3s
     ⠿ Container orderer.example.com           Started                            2.3s
     ⠿ Container cli                           Started                            4.1s
    CONTAINER ID   IMAGE       COMMAND      CREATED          STATUS                PORTS                NAMES
    70d6427003ae   hyperledger/fabric-tools:latest  "/bin/bash"  4 seconds ago    Up Less than a second   cli
    0f2f91968493   hyperledger/fabric-peer:latest      "peer node start"        4 seconds ago    Up 3 seconds                0.0.0.0:9051->9051/tcp, :::9051->9051/tcp, 7051/tcp, 0.0.0.0:9445->9445/tcp, :::9445->9445/tcp   peer0.org2.example.com
    615cea63009c   hyperledger/fabric-orderer:latest   "orderer"                4 seconds ago    Up 2 seconds                0.0.0.0:7050->7050/tcp, :::7050->7050/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp             orderer.example.com
    1db85f663965   hyperledger/fabric-peer:latest      "peer node start"        4 seconds ago    Up 1 second                 0.0.0.0:7051->7051/tcp, :::7051->7051/tcp, 0.0.0.0:9444->9444/tcp, :::9444->9444/tcp             peer0.org1.example.com
    d10bd7ff864d   hyperledger/explorer:latest         "docker-entrypoint.s…"   34 minutes ago   Exited (1) 33 minutes ago                                                                                                    explorer.mynetwork.com
    2ad7a1e8464e   hyperledger/explorer-db:latest      "docker-entrypoint.s…"   34 minutes ago   Up 34 minutes (healthy)     5432/tcp                                                                                         explorerdb.mynetwork.com
    efd328836573   portainer/portainer-ce              "/portainer"             3 days ago       Up About an hour            0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 9443/tcp   portainer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    最终出现以上输出日志则表示网络启动成功,每个加入Fabric网络的Node和User都需要隶属于某个组织,以上网络中包含了两个平行组织—peer0.org1.example.compeer0.org2.example.com,它还包括一个作为ordering service维护网络的orderer.example.com`。

    请添加图片描述
    请添加图片描述

    创建channel

    上节已经在机器上运行了peer节点和orderer节点,现在可以使用network.sh为Org1和Org2之间创建channel。channel是特定网络成员之间的私有通道,只能被属于该通道的组织使用,并且对网络的其他成员是不可见的。每个channel都有一个单独的区块链账本,属于该通道的组织可以让其下peer加入该通道,以让peer能够存储channel上的帐本并验证账本上的交易。
    使用以下命令创建自定义通道testchannel:

    $ ./network.sh createChannel -c testchannel
    
    • 1

    在这里插入图片描述
    在这里插入图片描述

    部署chaincode

    部署链码前,建议到链码子目录下执行go mod tidy,检查链码调用的包存在。同时,可能需要sudo apt install jq。创建通道后,您可以开始使用智能合约与通道账本交互。智能合约包含管理区块链账本上资产的业务逻辑,由成员运行的应用程序网络可以在账本上调用智能合约创建,更改和转让这些资产。可以通过./network.sh deployCC命令部署智能合约,但本过程可能会出现很多问题。使用以下命令部署chaincode:

    $ ./network.sh deployCC -c testchannel -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
    
    • 1

    此命令执行后可能会出现错误:scripts/deployCC.sh: line 114: log.txt: Permission denied,很明显这是权限不足所致,加上sudo试试:

    $sudo ./network.sh deployCC -c testchannel -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go
    
    • 1

    chaincode

    code1

    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: "cuteAlgernon", AppraisedValue: 300},
        {ID: "asset2", Color: "red", Size: 5, Owner: "Biosheep", AppraisedValue: 1000},
        {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)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197

    code2-1资产管理

    package atcc
    // 导入必要的依赖
    import (
      "fmt"
      "encoding/json"
      "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    type SmartContract struct {
      contractapi.Contract
    }
    // 定义资产的数据结构,并使用注解的方式来辅助序列化,marshal函数会使用字母序对key进行排序,这样可以
    // 保证其序列化之后具有唯一性,即不会出现导出的json字符串中ID字段在Color字段前面这种情况,
    // 这样做的主要原因是为了保证输入输出的唯一性,防止背书验证的时候失败。
    type Asset struct {
      AppraisedValue int    `json:"AppraisedValue"`
      Color          string `json:"Color"`
      ID             string `json:"ID"`
      Owner          string `json:"Owner"`
      Size           int    `json:"Size"`
    }
    // 使用数据对链码进行初始化。
    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
        }
        // 按照id存储序列化后的资产
        err = ctx.GetStub().PutState(asset.ID, assetJSON)
        if err != nil {
            return fmt.Errorf("failed to put to world state. %v", err)
        }
      }
      return nil
    }
    // 通过传入参数来创建一个账本上不存在的资产,这里有在后面实现的方法AssetExists来检查是否存在某个key为id的资产。
    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)
    }
    // 从账本中读取资产,调用GetState来实现
    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
    }
    // 更新资产,这里实现逻辑是根据传入参数创建一个新的资产并序列化,然后覆盖原来的资产。
    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)
    }
    // 删除资产,直接调用DelState函数来实现删除。
    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)
    }
    // 检查id对应的资产是否存在,判断能不能读取出value即可。
    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
    }
    // 资产转移,实质是修改资产结构体的owner字段。
    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)
    }
    // 读取全部资产,调用GetStateByRange函数来获取账本上的全部记录。
    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
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167

    code2-2

    assetsManager.go

    package main
    import (
    	"log"
    	"github.com/hyperledger/fabric-contract-api-go/contractapi"
    	"main/atcc"
    )
    
    func main() {
    	assetChaincode, err := contractapi.NewChaincode(&atcc.SmartContract{})
    	if err != nil {
    		log.Panicf("Error creating atcc chaincode: %v", err)
    	}
    
    	if err := assetChaincode.Start(); err != nil {
    		log.Panicf("Error starting atcc chaincode: %v", err)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    构建链码

    go mod tidy
    go mod vendor
    
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    使用chaincode

    初始化账本

    peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C $CHANNEL_NAME -n ${CC_NAME} --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"function":"InitLedger","Args":[]}'
    
    • 1

    获取当前资产

    peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C $CHANNEL_NAME -n ${CC_NAME} --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"GetAllAssets","Args":[]}'
    
    
    • 1
    • 2

    总结

    随着实验次数的增多,肉眼可见的是文件夹越来越混乱——比如这次的网络,需要在前几次实验中找到内容,而每次实验都涉及到文件创建、下载、删除,有时哪怕一次实验,都会鼓捣出很多奇奇怪怪的文件夹,有时会出现这样一种状况:我知道网络是通的,但不知道是那个文件能够跑通,毕竟,如果按照教程顺利过关,是对具体内容没有深刻印象的,直到需要再次使用的时候。

    链码编写(改写)难度不算特别大,但如果 涉及到网络上跑通,再加之账本的增删改查,如果找不到之前实验的基础内容,无异于再做一次。

  • 相关阅读:
    【前端实例代码】使用HTML CSS 和 JavaScript制作一个五星评价的功能!可动态好评+差评+留言功能!
    【done】剑指 Offer 53 - II:0~n-1中缺失的数字
    React 全栈体系(十一)
    优质记录片安利,记录片迷们快进来
    04_SpringBoot整合Mybatis
    Oracle-ADG部署
    纳什均衡求解器
    【PyCharm Community Edition】:分析map文件统计RAM及ROM的使用
    【01背包问题】
    【期权系列】期权市场 PCR 指标的策略应用
  • 原文地址:https://blog.csdn.net/Algernon98/article/details/127626102