首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Hyperledger Fabric链码开发基础篇

Hyperledger Fabric链码开发基础篇

作者头像
Zeal
发布于 2020-11-11 09:06:36
发布于 2020-11-11 09:06:36
1.3K0
举报
文章被收录于专栏:Hyperledger实践Hyperledger实践

参考

https://hyperledger-fabric.readthedocs.io/en/release-1.2/chaincode4ade.html

http://www.javatree.cn/news/1ac8329618a949eaa402ca5e9338dc2d

Chaincode链码是一个程序,可处理提交的查询或更新提议,用于维护记账本的状态。我们也称链码为智能合同, 它定义了业务逻辑。链码主要开发语言是Go和Node.js,而Java也会在不久支持。

链码主要遵循Shim API接口规范。

我们主要使用Go语言示范,Node.js安装编译有些慢甚至超时, 可能是一些依赖或网络限制,参考下BYFN中官方文档的说明。

Note

The instantiation of the Node.js chaincode will take roughly a minute. The command is not hanging; rather it is installing the fabric-shim layer as the image is being compiled.

1. BYFN 链码例子

路径/fabric-samples/chaincode/chaincode_example02/go/chaincode_example02.go

/*

Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

*/

package main

//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of

//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has

//to be modified as well with the new ID of chaincode_example02.

//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of

//hard-coding.

import (

"fmt"

"strconv"

"github.com/hyperledger/fabric/core/chaincode/shim"

pb "github.com/hyperledger/fabric/protos/peer"

)

// SimpleChaincode example simple Chaincode implementation

type SimpleChaincode struct {

}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {

fmt.Println("ex02 Init")

_, args := stub.GetFunctionAndParameters()

var A, B string // Entities

var Aval, Bval int // Asset holdings

var err error

if len(args) != 4 {

return shim.Error("Incorrect number of arguments. Expecting 4")

}

// Initialize the chaincode

A = args[0]

Aval, err = strconv.Atoi(args[1])

if err != nil {

return shim.Error("Expecting integer value for asset holding")

}

B = args[2]

Bval, err = strconv.Atoi(args[3])

if err != nil {

return shim.Error("Expecting integer value for asset holding")

}

fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

// Write the state to the ledger

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))

if err != nil {

return shim.Error(err.Error())

}

err = stub.PutState(B, []byte(strconv.Itoa(Bval)))

if err != nil {

return shim.Error(err.Error())

}

return shim.Success(nil)

}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {

fmt.Println("ex02 Invoke")

function, args := stub.GetFunctionAndParameters()

if function == "invoke" {

// Make payment of X units from A to B

return t.invoke(stub, args)

} else if function == "delete" {

// Deletes an entity from its state

return t.delete(stub, args)

} else if function == "query" {

// the old "Query" is now implemtned in invoke

return t.query(stub, args)

}

return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")

}

// Transaction makes payment of X units from A to B

func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {

var A, B string // Entities

var Aval, Bval int // Asset holdings

var X int // Transaction value

var err error

if len(args) != 3 {

return shim.Error("Incorrect number of arguments. Expecting 3")

}

A = args[0]

B = args[1]

// Get the state from the ledger

// TODO: will be nice to have a GetAllState call to ledger

Avalbytes, err := stub.GetState(A)

if err != nil {

return shim.Error("Failed to get state")

}

if Avalbytes == nil {

return shim.Error("Entity not found")

}

Aval, _ = strconv.Atoi(string(Avalbytes))

Bvalbytes, err := stub.GetState(B)

if err != nil {

return shim.Error("Failed to get state")

}

if Bvalbytes == nil {

return shim.Error("Entity not found")

}

Bval, _ = strconv.Atoi(string(Bvalbytes))

// Perform the execution

X, err = strconv.Atoi(args[2])

if err != nil {

return shim.Error("Invalid transaction amount, expecting a integer value")

}

Aval = Aval - X

Bval = Bval + X

fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

// Write the state back to the ledger

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))

if err != nil {

return shim.Error(err.Error())

}

err = stub.PutState(B, []byte(strconv.Itoa(Bval)))

if err != nil {

return shim.Error(err.Error())

}

return shim.Success(nil)

}

// Deletes an entity from state

func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {

if len(args) != 1 {

return shim.Error("Incorrect number of arguments. Expecting 1")

}

A := args[0]

// Delete the key from the state in ledger

err := stub.DelState(A)

if err != nil {

return shim.Error("Failed to delete state")

}

return shim.Success(nil)

}

// query callback representing the query of a chaincode

func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {

var A string // Entities

var err error

if len(args) != 1 {

return shim.Error("Incorrect number of arguments. Expecting name of the person to query")

}

A = args[0]

// Get the state from the ledger

Avalbytes, err := stub.GetState(A)

if err != nil {

jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"

return shim.Error(jsonResp)

}

if Avalbytes == nil {

jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"

return shim.Error(jsonResp)

}

jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"

fmt.Printf("Query Response:%s\n", jsonResp)

return shim.Success(Avalbytes)

}

func main() {

err := shim.Start(new(SimpleChaincode))

if err != nil {

fmt.Printf("Error starting Simple chaincode: %s", err)

}

}

(1) import导入shim和peer 依赖库

(2) 需要定义一个struct, 实现Init和Invoke两个函数

(3) Init函数在链码安装后实例化的时候会被调用,同时在链码版本升级的时候也会被调用用于数据迁移,需要小心实现。使用stub也是可以初始化时传入参数,例如:

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

(4) Invoke函数用于处理客户提议请求

stub shim.ChaincodeStubInterface是最常用的类,直接上接口源码, 参考注释。

// ChaincodeStubInterface is used by deployable chaincode apps to access and // modify their ledgers type ChaincodeStubInterface interface { // GetArgs returns the arguments intended for the chaincode Init and Invoke // as an array of byte arrays. GetArgs() [][]byte // GetStringArgs returns the arguments intended for the chaincode Init and // Invoke as a string array. Only use GetStringArgs if the client passes // arguments intended to be used as strings. GetStringArgs() []string // GetFunctionAndParameters returns the first argument as the function // name and the rest of the arguments as parameters in a string array. // Only use GetFunctionAndParameters if the client passes arguments intended // to be used as strings. GetFunctionAndParameters() (string, []string) // GetArgsSlice returns the arguments intended for the chaincode Init and // Invoke as a byte array GetArgsSlice() ([]byte, error) // GetTxID returns the tx_id of the transaction proposal, which is unique per // transaction and per client. See ChannelHeader in protos/common/common.proto // for further details. GetTxID() string // GetChannelID returns the channel the proposal is sent to for chaincode to process. // This would be the channel_id of the transaction proposal (see ChannelHeader // in protos/common/common.proto) except where the chaincode is calling another on // a different channel GetChannelID() string // InvokeChaincode locally calls the specified chaincode `Invoke` using the // same transaction context; that is, chaincode calling chaincode doesn't // create a new transaction message. // If the called chaincode is on the same channel, it simply adds the called // chaincode read set and write set to the calling transaction. // If the called chaincode is on a different channel, // only the Response is returned to the calling chaincode; any PutState calls // from the called chaincode will not have any effect on the ledger; that is, // the called chaincode on a different channel will not have its read set // and write set applied to the transaction. Only the calling chaincode's // read set and write set will be applied to the transaction. Effectively // the called chaincode on a different channel is a `Query`, which does not // participate in state validation checks in subsequent commit phase. // If `channel` is empty, the caller's channel is assumed. InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response // GetState returns the value of the specified `key` from the // ledger. Note that GetState doesn't read data from the writeset, which // has not been committed to the ledger. In other words, GetState doesn't // consider data modified by PutState that has not been committed. // If the key does not exist in the state database, (nil, nil) is returned. GetState(key string) ([]byte, error) // PutState puts the specified `key` and `value` into the transaction's // writeset as a data-write proposal. PutState doesn't effect the ledger // until the transaction is validated and successfully committed. // Simple keys must not be an empty string and must not start with null // character (0x00), in order to avoid range query collisions with // composite keys, which internally get prefixed with 0x00 as composite // key namespace. PutState(key string, value []byte) error // DelState records the specified `key` to be deleted in the writeset of // the transaction proposal. The `key` and its value will be deleted from // the ledger when the transaction is validated and successfully committed. DelState(key string) error // GetStateByRange returns a range iterator over a set of keys in the // ledger. The iterator can be used to iterate over all keys // between the startKey (inclusive) and endKey (exclusive). // The keys are returned by the iterator in lexical order. Note // that startKey and endKey can be empty string, which implies unbounded range // query on start or end. // Call Close() on the returned StateQueryIteratorInterface object when done. // The query is re-executed during validation phase to ensure result set // has not changed since transaction endorsement (phantom reads detected). GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) // GetStateByPartialCompositeKey queries the state in the ledger based on // a given partial composite key. This function returns an iterator // which can be used to iterate over all composite keys whose prefix matches // the given partial composite key. The `objectType` and attributes are // expected to have only valid utf8 strings and should not contain // U+0000 (nil byte) and U+10FFFF (biggest and unallocated code point). // See related functions SplitCompositeKey and CreateCompositeKey. // Call Close() on the returned StateQueryIteratorInterface object when done. // The query is re-executed during validation phase to ensure result set // has not changed since transaction endorsement (phantom reads detected). GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error) // CreateCompositeKey combines the given `attributes` to form a composite // key. The objectType and attributes are expected to have only valid utf8 // strings and should not contain U+0000 (nil byte) and U+10FFFF // (biggest and unallocated code point). // The resulting composite key can be used as the key in PutState(). CreateCompositeKey(objectType string, attributes []string) (string, error) // SplitCompositeKey splits the specified key into attributes on which the // composite key was formed. Composite keys found during range queries // or partial composite key queries can therefore be split into their // composite parts. SplitCompositeKey(compositeKey string) (string, []string, error) // GetQueryResult performs a "rich" query against a state database. It is // only supported for state databases that support rich query, // e.g.CouchDB. The query string is in the native syntax // of the underlying state database. An iterator is returned // which can be used to iterate (next) over the query result set. // The query is NOT re-executed during validation phase, phantom reads are // not detected. That is, other committed transactions may have added, // updated, or removed keys that impact the result set, and this would not // be detected at validation/commit time. Applications susceptible to this // should therefore not use GetQueryResult as part of transactions that update // ledger, and should limit use to read-only chaincode operations. GetQueryResult(query string) (StateQueryIteratorInterface, error) // GetHistoryForKey returns a history of key values across time. // For each historic key update, the historic value and associated // transaction id and timestamp are returned. The timestamp is the // timestamp provided by the client in the proposal header. // GetHistoryForKey requires peer configuration // core.ledger.history.enableHistoryDatabase to be true. // The query is NOT re-executed during validation phase, phantom reads are // not detected. That is, other committed transactions may have updated // the key concurrently, impacting the result set, and this would not be // detected at validation/commit time. Applications susceptible to this // should therefore not use GetHistoryForKey as part of transactions that // update ledger, and should limit use to read-only chaincode operations. GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) // GetPrivateData returns the value of the specified `key` from the specified // `collection`. Note that GetPrivateData doesn't read data from the // private writeset, which has not been committed to the `collection`. In // other words, GetPrivateData doesn't consider data modified by PutPrivateData // that has not been committed. GetPrivateData(collection, key string) ([]byte, error) // PutPrivateData puts the specified `key` and `value` into the transaction's // private writeset. Note that only hash of the private writeset goes into the // transaction proposal response (which is sent to the client who issued the // transaction) and the actual private writeset gets temporarily stored in a // transient store. PutPrivateData doesn't effect the `collection` until the // transaction is validated and successfully committed. Simple keys must not be // an empty string and must not start with null character (0x00), in order to // avoid range query collisions with composite keys, which internally get // prefixed with 0x00 as composite key namespace. PutPrivateData(collection string, key string, value []byte) error // DelState records the specified `key` to be deleted in the private writeset of // the transaction. Note that only hash of the private writeset goes into the // transaction proposal response (which is sent to the client who issued the // transaction) and the actual private writeset gets temporarily stored in a // transient store. The `key` and its value will be deleted from the collection // when the transaction is validated and successfully committed. DelPrivateData(collection, key string) error // GetPrivateDataByRange returns a range iterator over a set of keys in a // given private collection. The iterator can be used to iterate over all keys // between the startKey (inclusive) and endKey (exclusive). // The keys are returned by the iterator in lexical order. Note // that startKey and endKey can be empty string, which implies unbounded range // query on start or end. // Call Close() on the returned StateQueryIteratorInterface object when done. // The query is re-executed during validation phase to ensure result set // has not changed since transaction endorsement (phantom reads detected). GetPrivateDataByRange(collection, startKey, endKey string) (StateQueryIteratorInterface, error) // GetPrivateDataByPartialCompositeKey queries the state in a given private // collection based on a given partial composite key. This function returns // an iterator which can be used to iterate over all composite keys whose prefix // matches the given partial composite key. The `objectType` and attributes are // expected to have only valid utf8 strings and should not contain // U+0000 (nil byte) and U+10FFFF (biggest and unallocated code point). // See related functions SplitCompositeKey and CreateCompositeKey. // Call Close() on the returned StateQueryIteratorInterface object when done. // The query is re-executed during validation phase to ensure result set // has not changed since transaction endorsement (phantom reads detected). GetPrivateDataByPartialCompositeKey(collection, objectType string, keys []string) (StateQueryIteratorInterface, error) // GetPrivateDataQueryResult performs a "rich" query against a given private // collection. It is only supported for state databases that support rich query, // e.g.CouchDB. The query string is in the native syntax // of the underlying state database. An iterator is returned // which can be used to iterate (next) over the query result set. // The query is NOT re-executed during validation phase, phantom reads are // not detected. That is, other committed transactions may have added, // updated, or removed keys that impact the result set, and this would not // be detected at validation/commit time. Applications susceptible to this // should therefore not use GetQueryResult as part of transactions that update // ledger, and should limit use to read-only chaincode operations. GetPrivateDataQueryResult(collection, query string) (StateQueryIteratorInterface, error) // GetCreator returns `SignatureHeader.Creator` (e.g. an identity) // of the `SignedProposal`. This is the identity of the agent (or user) // submitting the transaction. GetCreator() ([]byte, error) // GetTransient returns the `ChaincodeProposalPayload.Transient` field. // It is a map that contains data (e.g. cryptographic material) // that might be used to implement some form of application-level // confidentiality. The contents of this field, as prescribed by // `ChaincodeProposalPayload`, are supposed to always // be omitted from the transaction and excluded from the ledger. GetTransient() (map[string][]byte, error) // GetBinding returns the transaction binding, which is used to enforce a // link between application data (like those stored in the transient field // above) to the proposal itself. This is useful to avoid possible replay // attacks. GetBinding() ([]byte, error) // GetDecorations returns additional data (if applicable) about the proposal // that originated from the peer. This data is set by the decorators of the // peer, which append or mutate the chaincode input passed to the chaincode. GetDecorations() map[string][]byte // GetSignedProposal returns the SignedProposal object, which contains all // data elements part of a transaction proposal. GetSignedProposal() (*pb.SignedProposal, error) // GetTxTimestamp returns the timestamp when the transaction was created. This // is taken from the transaction ChannelHeader, therefore it will indicate the // client's timestamp and will have the same value across all endorsers. GetTxTimestamp() (*timestamp.Timestamp, error) // SetEvent allows the chaincode to set an event on the response to the // proposal to be included as part of a transaction. The event will be // available within the transaction in the committed block regardless of the // validity of the transaction. SetEvent(name string, payload []byte) error }

常用的代码片段

function, args := stub.GetFunctionAndParameters()

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))

err := stub.DelState(A)

Avalbytes, err := stub.GetState(A)

对于State状态的查询,设置,删除操作。基本上把fabric-samples下的chaincode代码看一遍基能熟悉了。

2. 编译

可以使用命令行编译,主要是保证没有编译错误,但是无法测试业务逻辑。

编译也可以使用GoLand等IDE, 基本没提示出错就可以了。

3. devmode开发模式下测试

进入/fabric-samples/chaincode-docker-devmode

(1)在终端1启动网络

docker-compose -f docker-compose-simple.yaml up

(2) 在终端2编译和启动链码

这步是实际是在节点注册启动了。

docker exec -it chaincode bash

cd sacc

go build

启动链码

CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc

(3) 终端3中使用链码

安装和初始化链码在devmode是有点多余的,后面版本可能会删除。

docker exec -it cli bash

peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0

peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc

修改a的值

peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc

验证查询a的值

peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc

如果运行的是couchdb需要支持富查询,估计这个模式够呛,可能搭建个测试版本区块链直接开测也差不多的效率。

调试需要靠日志去判断。不过一般情况下,链码API也不多,逻辑一般不会太复杂,也可以适当的把复杂的逻辑移到客户应用端。

4. Hyperledger Composer开发模式

为了简化Fabric的配置和开发,主要是IBM团队维护了一套使用javascript定义asset资源, 参与者participant, 交易transaction为编程模型的区块链平台,还可以把链码接口很方便的暴露为REST服务,也提供了大量一个web系统playground和工具配置区块链网络,但同时也掩盖了很多Fabric的细节,做一些配置的又得绕回Fabric.

貌似推荐的服务器是UBUNTU, 开发模式是否能和Fabric混着用还不清楚,觉得直接用Fabric可能会稳定纯粹些,都有些取舍吧。

当然可以付费使用一些商业的区块链平台,运维自然也会方便很多。

https://ibm-blockchain.github.io/develop/installing/installing-index

最后链码应该也会有很多最佳实践,笔者的团队不断的实践当中,我们会在后面的日子不断的总结再和大家分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Hyperledger实践 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
HyperLedger Fabric 1.0的Transaction处理流程
如果把区块链比作一个只能读写,不能删改的分布式数据库的话,那么事务和查询就是对这个数据库进行的最重要的操作。以比特币来说,我们通过钱包或者Blockchain.info进行区块链的查询操作,而转账行为就是Transaction的处理。而HyperLedger Fabric在1.0对系统架构进行了升级,使得事务的处理更加复杂。
深蓝studyzy
2022/06/16
4970
HyperLedger Fabric 1.0的Transaction处理流程
Hyperledger fabric链码数据模型的探讨(1)之转账和交易
编写过一些链码的人可能会觉得是在操作一个简单的key-value数据库, 就是GetState和PutState去操作键值对,而对复杂些的一对多,多对多等实体关系和数据模型不知怎么设计。我们先从官方的例子入手一起探讨下。
Zeal
2020/11/11
1.2K0
hyperledger fabric1.0整体架构与记账逻辑架构的分析
3)数据何时进入orderer节点,orderer节点是如何处理的?(0.6里面就是共识这块怎么处理)
全栈程序员站长
2022/07/20
4870
hyperledger fabric1.0整体架构与记账逻辑架构的分析
区块链开源实现fabric快速部署及CLI体验
本文描述fabric快速部署的步骤,及演示基于官方example02的智能合约进行CLI命令行体验。区块链涉及服务很多,且大量使用docker容器技术,所以请严格遵守以下步骤去部署,以减少各种问题的出现,方便我们先对联盟链有个大概的感觉。本文描述环境是centos7操作系统,请其他版本更正相关的安装工具(如ubuntu操作系统请把yum命令换成apt-get)。
陶辉
2019/06/21
1.4K0
区块链开源实现fabric快速部署及CLI体验
Hyperledger Fabric Chaincode 开发
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>
netkiller old
2018/02/17
2.5K0
Hyperledger fabric Chaincode 开发详解
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>
netkiller old
2018/03/16
5.1K0
HyperLedger Fabric ChainCode开发——shim.ChaincodeStubInterface用法
深蓝前几篇博客讲了Fabric的环境搭建,在环境搭建好后,我们就可以进行Fabric的开发工作了。Fabric的开发主要分成2部分,ChainCode链上代码开发和基于SDK的Application开发。我们这里先讲ChainCode的开发。Fabric的链上代码支持Java或者Go语言进行开发,因为Fabric本身是Go开发的,所以深蓝建议还是用Go进行ChainCode的开发。
深蓝studyzy
2022/06/16
5270
智能合约DAPP系统开发代码流程部署方案
//invoke实现了两个键之间的value转移,输入参数为KEY1_NAME,KEY2_NAME,VALUE
系统_I8O28578624
2023/02/01
2980
Hyperledger Fabric 积分代币上链方案
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>
netkiller old
2018/03/19
7.5K5
区块链银行应用探索(Hyperledger fabric)
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>
netkiller old
2018/03/25
5.7K2
Fabric基础架构原理(4):链码 | 赠书活动
智能合约能够部署和运行在区块链环境中,由一段代码来描述相关的业务逻辑。部署后的智能合约在区块链中无法修改,智能合约的执行完全由代码决定,不受人为因素的干扰。一般来说,参与方通过智能合约规定各自权利和义务、触发合约的条件以及结果,一旦该智能合约在区块链环境中运行就可以得出客观、准确的结果。
Henry Zhang
2019/04/12
6030
Fabric基础架构原理(4):链码 | 赠书活动
hyperledger fabric 开发第一个智能合约
一、编写智能合约代码HelloWorld.go,go语言实现,代码很简单,每个合约包含两个方法,Init、Invoke。
Tiny熊
2020/08/27
8850
hyperledger fabric 开发第一个智能合约
Hyperledger Fabric 1.3正式版新特性
https://github.com/hyperledger/fabric/releases
Zeal
2020/11/11
8170
Hyperledger Fabric 1.3正式版新特性
Hyperledger Fabric私有数据
Fabric区块链网络一个channel即一个记账本, 在很多业务场景,一个记账本的数据自身组织可以读写,也可以提供给其它组织只读,部分读或部分写。数据隔离使用channel是粗粒度的,private data私有数据是fabric 1.2引入, 是为了在更细的粒度上控制数据访问。
Zeal
2020/11/11
6070
Hyperledger Fabric私有数据
Hyperledger Fabric中的零知识证明
Fabric 1.3中的新增的idemixer(Identity Mixer)以前不大懂zero-knowledge proof(零知识证明),原本觉得PKI基础的MSP是比较常用和稳健的方式,新加个验证方式是不是有点增加复杂性。
Zeal
2020/11/11
1.8K0
Hyperledger Fabric中的零知识证明
快速带你上手Hyperledger Fabric环境搭建+开发测试
本文采用virtualbox+vagrant方式搭建Hyperledger Fabric环境,采用VSCode远程开发模式开发调试链码和客户端程序。
jasonruan
2020/08/12
4.6K0
Hyperledger Fabric Go SDK 事件分析
最近在做跨链适配器,需要在一条本地链上利用 Go SDK 来连接 fabric 网络,并监听事件,所以对 fabric 所支持的事件与 SDK 所提供的监听方法做一下汇总。
pseudoyu
2023/04/11
1.1K0
Hyperledger Fabric Go SDK 事件分析
在Fabric ChainCode中导入第三方包(以状态机为例)
在企业级应用开发中,经常会涉及到流程和状态,而有限状态机(FSM)则是对应的一种简单实现,如果复杂化,就上升到Workflow和BPM了。我们在Fabric ChainCode的开发过程中,也很可能涉及到状态机,这里我们就举一个例子,用FSM实现一个二级审批的状态转移。
深蓝studyzy
2022/06/16
2990
在Fabric ChainCode中导入第三方包(以状态机为例)
fabric区块链(四)—chaincode接口
酒楼
2023/05/30
2590
BitXHub 跨链插件(Fabric)源码解读
之前提到过趣链科技的 BitXHub 跨链平台是业界较为完善的跨链开源解决方案,主要通过中继链、网关和插件机制对跨链流程中的功能、安全性和灵活性等进行了优化。
pseudoyu
2023/04/11
6220
BitXHub 跨链插件(Fabric)源码解读
推荐阅读
相关推荐
HyperLedger Fabric 1.0的Transaction处理流程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档