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

视频8

https://pkg.go.dev/github.com/hyperledger/fabric-chaincode-go/shim#section-sourcefiles

简单资产链码

我们的应用程序是一个基本的示例链码,用来在账本上创建资产(键-值对)。

选择一个位置存放代码

如果你没有写过 Go 的程序,你可能需要确认一下你是否安装了 Go 以及你的系统是否配置正确。我们假设你用的是支持模块的版本。

现在你需要为你的链码应用程序创建一个目录。

简单起见,我们使用如下命令:

mkdir sacc && cd sacc

现在,我们创建一个用于编写代码的源文件:

go mod init sacc
touch sacc.go

内务

首先,我们从内务开始。每一个链码都要实现 Chaincode 接口 中的 InitInvoke 方法。所以,我们先使用 Go import 语句来导入链码必要的依赖。我们将导入链码 shim 包和 peer protobuf 包 。然后,我们加入一个 SimpleAsset 结构体来作为 Chaincode shim 方法的接收者。

package main

import (
    "fmt"

    "github.com/hyperledger/fabric-chaincode-go/shim"
    "github.com/hyperledger/fabric-protos-go/peer"
)

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}

初始化链码

然后,我们将实现 Init 方法。

// Init is called during chaincode instantiation to initialize any data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {

}

注解

注意,链码升级的时候也要调用这个方法。当写一个用来升级已存在的链码的时候,请确保合理更改 Init 方法。特别地,当升级时没有“迁移”或者没东西需要初始化时,可以提供一个空的 Init 方法。

接下来,我们将使用 ChaincodeStubInterface.GetStringArgs 方法获取 Init 调用的参数,并且检查其合法性。在我们的用例中,我们希望得到一个键-值对。

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
  // Get the args from the transaction proposal
  args := stub.GetStringArgs()
  if len(args) != 2 {
    return shim.Error("Incorrect arguments. Expecting a key and a value")
  }
}

请添加图片描述

接下来,我们已经确定了调用是合法的,我们将把初始状态存入账本中。我们将调用 ChaincodeStubInterface.PutState 并将键和值作为参数传递给它。假设一切正常,将返回一个 peer.Response 对象,表明初始化成功。

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
  // Get the args from the transaction proposal
  args := stub.GetStringArgs()
  if len(args) != 2 {
    return shim.Error("Incorrect arguments. Expecting a key and a value")
  }

  // Set up any variables or assets here by calling stub.PutState()

  // We store the key and the value on the ledger
  err := stub.PutState(args[0], []byte(args[1]))
  if err != nil {
    return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
  }
  return shim.Success(nil)
}

调用链码

首先,我们增加一个 Invoke 函数的签名。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The 'set'
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

}

就像上边的 Init 函数一样,我们需要从 ChaincodeStubInterface 中解析参数。Invoke 函数的参数是将要调用的链码应用程序的函数名。在我们的用例中,我们的应用程序将有两个方法: setget ,用来设置或者获取资产当前的状态。我们先调用 ChaincodeStubInterface.GetFunctionAndParameters 来为链码应用程序的方法解析方法名和参数。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

}

然后,我们将验证函数名是否为 set 或者 get ,并执行链码应用程序的方法,通过 shim.Successshim.Error 返回一个适当的响应,这个响应将被序列化为 gRPC protobuf 消息。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == "set" {
            result, err = set(stub, args)
    } else {
            result, err = get(stub, args)
    }
    if err != nil {
            return shim.Error(err.Error())
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}

实现链码应用程序

就像我们说的,我们的链码应用程序实现了两个功能,它们可以通过 Invoke 方法调用。我们现在来实现这些方法。注意我们之前提到的,要访问账本状态,我们需要使用链码 shim API 中的 ChaincodeStubInterface.PutStateChaincodeStubInterface.GetState 方法。

// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 2 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
    }

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
            return "", fmt.Errorf("Failed to set asset: %s", args[0])
    }
    return args[1], nil
}

// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 1 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key")
    }

    value, err := stub.GetState(args[0])
    if err != nil {
            return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
    }
    if value == nil {
            return "", fmt.Errorf("Asset not found: %s", args[0])
    }
    return string(value), nil
}

把它们组合在一起

最后,我们增加一个 main 方法,它将调用 shim.Start 方法。下边是我们链码程序的完整源码。

package main

import (
    "fmt"

    "github.com/hyperledger/fabric-chaincode-go/shim"
    "github.com/hyperledger/fabric-protos-go/peer"
)

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
    // Get the args from the transaction proposal
    args := stub.GetStringArgs()
    if len(args) != 2 {
            return shim.Error("Incorrect arguments. Expecting a key and a value")
    }

    // Set up any variables or assets here by calling stub.PutState()

    // We store the key and the value on the ledger
    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
            return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
    }
    return shim.Success(nil)
}

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == "set" {
            result, err = set(stub, args)
    } else { // assume 'get' even if fn is nil
            result, err = get(stub, args)
    }
    if err != nil {
            return shim.Error(err.Error())
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}

// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 2 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
    }

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil {
            return "", fmt.Errorf("Failed to set asset: %s", args[0])
    }
    return args[1], nil
}

// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != 1 {
            return "", fmt.Errorf("Incorrect arguments. Expecting a key")
    }

    value, err := stub.GetState(args[0])
    if err != nil {
            return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
    }
    if value == nil {
            return "", fmt.Errorf("Asset not found: %s", args[0])
    }
    return string(value), nil
}

// main function starts up the chaincode in the container during instantiate
func main() {
    if err := shim.Start(new(SimpleAsset)); err != nil {
            fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
    }
}

请添加图片描述

链码访问控制

链码可以通过调用 GetCreator() 方法来使用客户端(提交者)证书进行访问控制决策。另外,Go shim 提供了扩展 API ,用于从提交者的证书中提取客户端标识用于访问控制决策,该证书可以是客户端身份本身,或者组织身份,或客户端身份属性。

例如,一个以键-值对表示的资产可以将客户端的身份作为值的一部分保存其中(比如以 JSON 属性标识资产主人),以后就只有被授权的客户端才可以更新键-值对。

详细信息请查阅 client identity (CID) library documentation

To add the client identity shim extension to your chaincode as a dependency, see 管理 Go 链码的扩展依赖.

将客户端身份 shim 扩展作为依赖添加到你的链码,请查阅 管理 Go 链码的扩展依赖

管理 Go 链码的扩展依赖

你的 Go 链码需要 Go 标准库之外的一些依赖包(比如链码 shim)。当链码安装到 peer 的时候,这些报的源码必须被包含在你的链码包中。如果你将你的链码构造为一个模块,最简单的方法就是在打包你的链码之前使用 go mod vendor 来 “vendor” 依赖。

go mod tidy
go mod vendor

这就把你链码的扩展依赖放进了本地的 vendor 目录。

当依赖都引入到你的链码目录后, peer chaincode packagepeer chaincode install 操作将把这些依赖一起放入链码包中。

视频9

拉取项目

GOPATH`设置为`/root/go` 进入`GOPATH/src
cd $GOPATH/src && git clone https://github.com/sxguan/fabric-go-sdk.git

启动节点

cd ./fabric-go-sdk/fixtures/ && docker-compose up -d

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

启动项目

cd .. && go build && ./fabric-go-sdk
>> 开始创建通道......
>>>> 使用每个org的管理员身份更新锚节点配置...
>>>> 使用每个org的管理员身份更新锚节点配置完成
>> 创建通道成功
>> 加入通道......
>> 加入通道成功
>> 开始打包链码......
>> 打包链码成功
>> 开始安装链码......
>> 安装链码成功
>> 组织认可智能合约定义......
>>> chaincode approved by Org1 peers:
	peer0.org1.example.com:7051
	peer1.org1.example.com:9051
>> 组织认可智能合约定义完成
>> 检查智能合约是否就绪......
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
>> 智能合约已经就绪
>> 提交智能合约定义......
>> 智能合约定义提交完成
>> 调用智能合约初始化方法......
>> 完成智能合约初始化
>> 通过链码外部服务设置链码状态......
>> 设置链码状态完成
<--- 添加信息 --->: 18c0c86ce029d7de04461484976c5151992864b52ca28905d0ccf911443fdfcb
<--- 查询信息 --->: 123

请添加图片描述

algernon@algernon-Lenovo-Legion-Y7000:/opt/gopath/src$ go build && ./fabric-go-sdk
github.com/hyperledger/fabric-chaincode-go/shim/handler.go:11:2: cannot find package "github.com/golang/protobuf/proto" in any of:
	/usr/local/go/src/github.com/golang/protobuf/proto (from $GOROOT)
	/opt/gopath/src/github.com/golang/protobuf/proto (from $GOPATH)
github.com/hyperledger/fabric-protos-go/peer/snapshot.pb.go:10:2: cannot find package "github.com/golang/protobuf/ptypes/empty" in any of:
	/usr/local/go/src/github.com/golang/protobuf/ptypes/empty (from $GOROOT)
	/opt/gopath/src/github.com/golang/protobuf/ptypes/empty (from $GOPATH)
github.com/hyperledger/fabric-chaincode-go/shim/interfaces.go:7:2: cannot find package "github.com/golang/protobuf/ptypes/timestamp" in any of:
	/usr/local/go/src/github.com/golang/protobuf/ptypes/timestamp (from $GOROOT)
	/opt/gopath/src/github.com/golang/protobuf/ptypes/timestamp (from $GOPATH)
github.com/hyperledger/fabric-protos-go/peer/chaincode_shim.pb.go:11:2: cannot find package "google.golang.org/grpc" in any of:
	/usr/local/go/src/google.golang.org/grpc (from $GOROOT)
	/opt/gopath/src/google.golang.org/grpc (from $GOPATH)
github.com/hyperledger/fabric-protos-go/peer/chaincode_shim.pb.go:12:2: cannot find package "google.golang.org/grpc/codes" in any of:
	/usr/local/go/src/google.golang.org/grpc/codes (from $GOROOT)
	/opt/gopath/src/google.golang.org/grpc/codes (from $GOPATH)
github.com/hyperledger/fabric-chaincode-go/shim/internal/client.go:13:2: cannot find package "google.golang.org/grpc/credentials" in any of:
	/usr/local/go/src/google.golang.org/grpc/credentials (from $GOROOT)
	/opt/gopath/src/google.golang.org/grpc/credentials (from $GOPATH)
github.com/hyperledger/fabric-chaincode-go/shim/internal/client.go:14:2: cannot find package "google.golang.org/grpc/keepalive" in any of:
	/usr/local/go/src/google.golang.org/grpc/keepalive (from $GOROOT)
	/opt/gopath/src/google.golang.org/grpc/keepalive (from $GOPATH)
github.com/hyperledger/fabric-protos-go/peer/chaincode_shim.pb.go:13:2: cannot find package "google.golang.org/grpc/status" in any of:
	/usr/local/go/src/google.golang.org/grpc/status (from $GOROOT)
	/opt/gopath/src/google.golang.org/grpc/status (from $GOPATH)

 go build && ./fabric-go-sdk
>> 开始创建通道......
>> Create channel and join error: Create channel error: error should be nil for SaveChannel of orgchannel: create channel failed: create channel failed: SendEnvelope failed: calling orderer 'orderer.example.com:7050' failed: Orderer Client Status Code: (2) CONNECTION_FAILED. Description: dialing connection on target [orderer.example.com:7050]: connection is in TRANSIENT_FAILURE


请添加图片描述

go build && ./fabric-go-sdk
>> 开始创建通道......
>> Create channel and join error: Create channel error: error should be nil for SaveChannel of orgchannel: create channel failed: create channel failed: SendEnvelope failed: calling orderer 'orderer.example.com:7050' failed: Orderer Server Status Code: (400) BAD_REQUEST. Description: error applying config update to existing channel 'mychannel': error authorizing update: error validating ReadSet: proposed update requires that key [Group]  /Channel/Application be at version 0, but it is currently at version 1

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

go build && ./fabric-go-sdk
>> 开始创建通道......
>>>> 使用每个org的管理员身份更新锚节点配置...
>>>> 使用每个org的管理员身份更新锚节点配置完成
>> 创建通道成功
>> 加入通道......
>> 加入通道成功
>> 开始打包链码......
>> 打包链码成功
>> 开始安装链码......
 [fabsdk/fab] 2022/10/26 15:19:14 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
 [fabsdk/fab] 2022/10/26 15:19:14 UTC - peer.(*peerEndorser).sendProposal -> ERRO process proposal failed [rpc error: code = DeadlineExceeded desc = context deadline exceeded]
>> create chaincode lifecycle error: %v installCC error: LifecycleInstallCC error: Multiple errors occurred: - Transaction processing for endorser [peer0.org1.example.com:7051]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded - Transaction processing for endorser [peer1.org1.example.com:9051]: gRPC Transport Status Code: (4) DeadlineExceeded. Description: context deadline exceeded

go build && ./fabric-go-sdk
>> 开始创建通道......
>>>> 使用每个org的管理员身份更新锚节点配置...
>>>> 使用每个org的管理员身份更新锚节点配置完成
>> 创建通道成功
>> 加入通道......
>> 加入通道成功
>> 开始打包链码......
>> 打包链码成功
>> 开始安装链码......
>> 安装链码成功
>> 组织认可智能合约定义......
>>> chaincode approved by Org1 peers:
	peer0.org1.example.com:7051
	peer1.org1.example.com:9051
>> 组织认可智能合约定义完成
>> 检查智能合约是否就绪......
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
>> 智能合约已经就绪
>> 提交智能合约定义......
>> 智能合约定义提交完成
>> 调用智能合约初始化方法......
>> 完成智能合约初始化
>> 通过链码外部服务设置链码状态......
>> 设置链码状态完成
<--- 添加信息 --->: 18c0c86ce029d7de04461484976c5151992864b52ca28905d0ccf911443fdfcb
<--- 查询信息 --->123

完整内容

https://sxguan0529.gitbook.io/hyperledger-fabric/fabric-sdk-go#san-pei-zhi-wen-jian-config.yaml

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值