hyperledger fabric 智能合约开发(三)------合约接口的数据操作方法

fabric链码编写(一):常用接口(以fabcar.go为例) fabric链码编写思路和常见的API整理: type SmartContract struct { contractapi.Contract } contractapi.Contract :关于资产调用的常用接口,很多官方链码都使用了,自己编写链码的时候可以不用,后面的都换成Stub()方法即可 首先需要init方法进行初始化: func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) er 阅读详情

合约接口的数据操作方法

写入区块链数据如果是 struct 结构体,需要序列化成二进制,通常使用 json,其他形式的序列化也可以,只要能反序列化即可(反序列化,是二进制数组变为格式化数据)。Hyperledger fabric levelDB提供基于key/value的数据存储,其中key是字符串,value则是二进制字节数组,Hyperledger的Go API提供了三个方法用于数据存取:PutState(key, value)用于向Hyperledger中存储数据, GetState(key)用于从Hyperledger中提取数据,而DelState(key)则从Hyperledger中删除数据。

1.PutState(key, value)写入区块,首先确定写入数据结构,其次从输入参数中获得string数据,可以选择检查数据是否存在,然后把输入参数写入数据结构,同时将数据结构(值)转化为json序列化数据,即二进制数组,将string类型key和序列化二进制数组写入数据库。

...

type marble struct {

ObjectType string `json:"docType"`

Name       string `json:"name"`

Color      string `json:"color"`

Size       int    `json:"size"`

Owner      string `json:"owner"`

}

...

marbleName := args[0]

color := strings.ToLower(args[1])

owner := strings.ToLower(args[3])

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

if err != nil {

return shim.Error("3rd argument must be a numeric string")

}

...

//检查要输入的数据是否已经存在

marbleAsBytes, err := stub.GetState(marbleName)

if err != nil {

return shim.Error("Failed to get marble: " + err.Error())

} else if marbleAsBytes != nil {

fmt.Println("This marble already exists: " + marbleName)

return shim.Error("This marble already exists: " + marbleName)

}

...

objectType := "marble"

marble := &marble{objectType, marbleName, color, size, owner}

marbleJSONasBytes, err := json.Marshal(marble)

if err != nil {

return shim.Error(err.Error())

}

...

err = stub.PutState(marbleName, marbleJSONasBytes)

if err != nil {

return shim.Error(err.Error())

}

...

2.GetState(key) 读取区块,通过key获取区块信息,返回的信息是byte数组,我们需要Json反序列化,可以得到我们想要的对象具体信息。

dbStudentBytes,err:= stub.GetState(key)

var dbStudent Student;

err=json.Unmarshal(dbStudentBytes,&dbStudent)//反序列化

if err != nil {

   return shim.Error("{\"Error\":\"Failed to decode JSON of: " + string(dbStudentBytes)+ "\" to Student}")

}

fmt.Println("Read Student from DB, name:"+dbStudent.Name)

【注意:不能在一个ChainCode函数中PutState后又马上GetState,这个时候GetState是没有最新值的,因为在这时Transaction并没有完成,还没有提交到StateDB里面】

3.传入参数stub shim.ChaincodeStubInterface,这个参数提供的接口为我们编写ChainCode的业务逻辑提供了大量实用的方法,如下:

GetArgs() [][]byte 以byte数组的数组的形式获得传入的参数列表

GetStringArgs() []string 以字符串数组的形式获得传入的参数列表

GetFunctionAndParameters() (string, []string) 将字符串数组的参数分为两部分,数组第一个字是Function,剩下的都是Parameter

GetArgsSlice() ([]byte, error) 以byte切片的形式获得参数列表

3.DelState(key) 删除区块,用来删除区块信息。根据Key删除State DB的数据。如果根据Key找不到对应的数据,删除失败。删除是删去stateDB数据库,而区块数据无法删除。

err= stub.DelState(key)

if err != nil {

   return shim.Error("Failed to delete Student from DB, key is: "+key)

}

4.数据修改,State 数据库并没有提供修改功能,修改数据可以先读取,再修改,最后写入。

func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {

if len(args) != 3 {

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

}

tokenAsBytes, _ := stub.GetState(args[0])

token := Token{}

json.Unmarshal(tokenAsBytes, &token)

token.transfer(args[1],args[2],args[3])

tokenAsBytes, _ = json.Marshal(token)

stub.PutState(args[0], tokenAsBytes)

return shim.Success(nil)

}

在区块链中,现在新增数据与原有的数据都存在,但在state DB只保存最新的数据,也就是最新的替代数据。

5.GetStateByRange(startKey, endKey) 范围查找,由于返回的是一个StateQueryIteratorInterface接口,我们在下例使用通用的接口而不是查询接口,我们需要通过这个接口再做一个for循环,才能读取返回的信息,所有我们可以独立出一个方法,专门将该接口返回的数据以string的byte数组形式返回。这是我们的转换方法:

func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {

 

startKey := "CAR0"

endKey := "CAR999"

//获取byte数组形式返回二进制数组

resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)

if err != nil {

return shim.Error(err.Error())

}

defer resultsIterator.Close()//关闭数据接受

//buffer是一组Json数组,需要转化为string格式数据

var buffer bytes.Buffer

buffer.WriteString("[")

//是否数组写入完毕

bArrayMemberAlreadyWritten := false

//有数据就一直接受数据

for resultsIterator.HasNext() {

queryResponse, err := resultsIterator.Next()

if err != nil {

return shim.Error(err.Error())

}

// 在数组成员之前添加逗号,为第一个数组成员禁止它

if bArrayMemberAlreadyWritten == true {

buffer.WriteString(",")

}

buffer.WriteString("{\"Key\":")

buffer.WriteString("\"")

buffer.WriteString(queryResponse.Key)

buffer.WriteString("\"")

//值

buffer.WriteString(", \"Record\":")

//Record是一个JSON对象,所以我们按原样编写

buffer.WriteString(string(queryResponse.Value))

buffer.WriteString("}")

bArrayMemberAlreadyWritten = true

}

buffer.WriteString("]")

 

fmt.Printf("- queryAllCars:\n%s\n", buffer.String())

//接口只能返回二进制数组

return shim.Success(buffer.Bytes())

}

6. 富查询GetQueryResult(query string) (StateQueryIteratorInterface, error),是对Value的内容进行查询,如果是LevelDB,那么是不支持,只有CouchDB时才能用这个方法。

关于传入的query这个字符串,其实是CouchDB所使用的Mango查询,我们可以在官方博客了解到一些信息:https://blog.couchdb.org/2016/08/03/feature-mango-query/ 其基本语法可以在https://github.com/cloudant/mango 这里看到。

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

   name:="Neo Chen" //需要查询的名字

   queryString := fmt.Sprintf("{\"selector\":{\"Name\":\"%s\"}}", name)

   resultsIterator,err:= stub.GetQueryResult(queryString)//必须是CouchDB才行

   if err!=nil{

      return shim.Error("query failed")

   }

   person,err:=getListResult(resultsIterator)

   if err!=nil{

      return shim.Error("query failed")

   }

   return shim.Success(person)

}

7.历史数据查询GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error),比如之前的Student:1这个对象,我们更改和删除过数据,现在要查询这个对象的更改记录,直接返回区块链上数据,那么对应代码为:

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

   student1:=Student{1,"Devin Zeng"}

   key:="Student:"+strconv.Itoa(student1.Id)

   it,err:= stub.GetHistoryForKey(key)

   if err!=nil{

      return shim.Error(err.Error())

   }

   var result,_= getHistoryListResult(it)

   return shim.Success(result)

}

func getHistoryListResult(resultsIterator shim.HistoryQueryIteratorInterface) ([]byte,error){

 

   defer resultsIterator.Close()

   // buffer is a JSON array containing QueryRecords

   var buffer bytes.Buffer

   buffer.WriteString("[")

 

   bArrayMemberAlreadyWritten := false

   for resultsIterator.HasNext() {

      queryResponse, err := resultsIterator.Next()

      if err != nil {

         return nil, err

      }

      // Add a comma before array members, suppress it for the first array member

      if bArrayMemberAlreadyWritten == true {

         buffer.WriteString(",")

      }

      item,_:= json.Marshal( queryResponse)

      buffer.Write(item)

      bArrayMemberAlreadyWritten = true

   }

   buffer.WriteString("]")

   fmt.Printf("queryResult:\n%s\n", buffer.String())

   return buffer.Bytes(), nil

}

8.生成复合键CreateCompositeKey(objectType string, attributes []string) (string, error),,ChainCode也为我们提供了生成Key的方法CreateCompositeKey,通过这个方法,我们可以将联合主键涉及到的属性都传进去,并声明了对象的类型即可。

以选课表为例,里面包含了以下属性:

type ChooseCourse struct {

   CourseNumber string //开课编号

   StudentId int //学生ID

   Confirm bool //是否确认

}

其中CourseNumber+StudentId构成了这个对象的联合主键,我们要获得生成的复核主键,那么可写为:

cc:=ChooseCourse{"CS101",123,true}  

var key1,_= stub.CreateCompositeKey("ChooseCourse",[]string{cc.CourseNumber,strconv.Itoa(cc.StudentId)})

fmt.Println(key1)

【注:其实Fabric就是用U+0000来把各个字段分割开的,因为这个字符太特殊,所以很适合做分割】

9.拆分复合键SplitCompositeKey(compositeKey string) (string, []string, error)

既然有组合那么就有拆分,当我们从数据库中获得了一个复合键的Key之后,怎么知道其具体是由哪些字段组成的呢。其实就是用U+0000把这个复合键再Split开,得到结果中第一个是objectType,剩下的就是复合键用到的列的值。

objType,attrArray,_:= stub.SplitCompositeKey(key1)

fmt.Println("Object:"+objType+" ,Attributes:"+strings.Join(attrArray,"|"))

10. 部分复合键的查询GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error)

这里其实是一种对Key进行前缀匹配的查询,也就是说,我们虽然是部分复合键的查询,但是不允许拿后面部分的复合键进行匹配,必须是前面部分。

11.stub.SetEvent(key, value) 事件,Hyperledger Fabic 事件实现了发布/订阅消息队列。您可以自由地在链码中创建和发出自定义事件。例如,区块链的状态发生改变,就会生成一个事件。通过向区块链上的事件中心注册一个事件适配器,客户端应用程序可以订阅和使用这些事件。也就是说当ChainCode提交完毕,会通过Event的方式通知Client。而通知的内容可以通过SetEvent设置。事件设置完毕后,需要在客户端也做相应的修改。

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

   message := "Event send data is here!"

   err := stub.SetEvent("evtsender", []byte(message))

   if err != nil {

      return shim.Error(err.Error())

   }

   return shim.Success(nil)

}

 

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

event := &Token{

Owner: "netkiller",

TotalSupply: 10000,

TokenName: "代币通正",

TokenSymbol: "COIN",

BalanceOf: map[string]uint{}}

 

    eventBytes, err ;= json.Marshal(&event)

    if err != nil {

            return nil, err

    }

    err := stub.SetEvent("evtSender", eventBytes)

    if err != nil {

        fmt.Println("Could not set event for loan application creation", err)

    }

}

12.stub.GetCreator() 获得证书资料,这个方法可以获得调用这个ChainCode的客户端的用户的证书,这里虽然返回的是byte数组,但是其实是一个字符串,内容格式如下:

-----BEGIN CERTIFICATE-----

MIICGjCCAcCgAwIBAgIRAMVe0+QZL+67Q+R2RmqsD90wCgYIKoZIzj0EAwIwczEL

MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG

cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh

Lm9yZzEuZXhhbXBsZS5jb20wHhcNMTcwODEyMTYyNTU1WhcNMjcwODEwMTYyNTU1

WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN

U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWVXNlcjFAb3JnMS5leGFtcGxlLmNvbTBZ

MBMGByqGSM49AgEGCCqGSM49AwEHA0IABN7WqfFwWWKynl9SI87byp0SZO6QU1hT

JRatYysXX5MJJRzvvVsSTsUzQh5jmgwkPbFcvk/x4W8lj5d2Tohff+WjTTBLMA4G

A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIO2os1zK9BKe

Lb4P8lZOFU+3c0S5+jHnEILFWx2gNoLkMAoGCCqGSM49BAMCA0gAMEUCIQDAIDHK

gPZsgZjzNTkJgglZ7VgJLVFOuHgKWT9GbzhwBgIgE2YWoDpG0HuhB66UzlA+6QzJ

+jvM0tOVZuWyUIVmwBM=

-----END CERTIFICATE-----

我们常见的需求是在ChainCode中获得当前用户的信息,方便进行权限管理。那么我们怎么获得当前用户呢?我们可以把这个证书的字符串转换为Certificate对象。一旦转换成这个对象,我们就可以通过Subject获得当前用户的名字。

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

   creatorByte,_:= stub.GetCreator()

   certStart := bytes.IndexAny(creatorByte, "-----BEGIN")

   if certStart == -1 {

      fmt.Errorf("No certificate found")

   }

   certText := creatorByte[certStart:]

   bl, _ := pem.Decode(certText)

   if bl == nil {

      fmt.Errorf("Could not decode the PEM structure")

   }

 

   cert, err := x509.ParseCertificate(bl.Bytes)

   if err != nil {

      fmt.Errorf("ParseCertificate failed")

   }

   uname:=cert.Subject.CommonName

   fmt.Println("Name:"+uname)

   return shim.Success([]byte("Called testCertificate "+uname))

}

13.调用其他链码,我们的链上代码中调用别人已经部署好的链上代码。比如官方提供的example02,我们要在代码中去实现a->b的转账,这里需要注意,我们使用的是example02的链上代码的实例名mycc,而不是代码的名字example02,那么我们的代码应该如下:

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

 //全部转化byte数组,调用连码需要提供连码名和通道名 stub.InvokeChaincode("连码名",调用函数,"通道")

   trans:=[][]byte{[]byte("invoke"),[]byte("a"),[]byte("b"),[]byte("11")}

   response:= stub.InvokeChaincode("mycc",trans,"mychannel")

   fmt.Println(response.Message)

   return shim.Success([]byte( response.Message))

}

例如以下程序

//将string参数转化为byte数组

func toChaincodeArgs(args ...string) [][]byte {

bargs := make([][]byte, len(args))

for i, arg := range args {

bargs[i] = []byte(arg)

}

return bargs

}

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

var sum, channelName string // Sum entity

var Aval int  // value of sum entity - to be computed

chaincodeName := args[0]

sum = args[1]

f := "index"

queryArgs := toChaincodeArgs(f,sum) //进行byte[]转化

response := stub.InvokeChaincode(chaincodeName, queryArgs, channelName)

Aval, err = strconv.Atoi(string(response.Payload))  

return shim.Success([]byte(strconv.Itoa(Aval)))

}

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

function, args := stub.GetFunctionAndParameters()

if function == "queryIndex" {

return t.query(stub, args)

}

return shim.Success([]byte("Invalid invoke function name. Expecting  \"query\""))

}

14.获得提案对象Proposal属性, 获得签名的提案GetSignedProposal() (*pb.SignedProposal, error)

从客户端发现背书节点的Transaction或者Query都是一个提案,GetSignedProposal获得当前的提案对象包括客户端对这个提案的签名。提案的内容如果直接打印出来感觉就像是乱码,其内包含了提案Header,Payload和Extension,里面更包含了复杂的结构,这里不讲,以后可以写一篇博客专门研究提案对象。

15.获得Transient对象 GetTransient() (map[string][]byte, error),Transient是在提案中Payload对象中的一个属性,也就是ChaincodeProposalPayload.TransientMap

16.获得交易时间戳GetTxTimestamp() (*timestamp.Timestamp, error),交易时间戳也是在提案对象中获取的,提案对象的Header部分,也就是proposal.Header.ChannelHeader.Timestamp

17.获得Binding对象 GetBinding() ([]byte, error),这个Binding对象也是从提案对象中提取并组合出来的,其中包含proposal.Header中的SignatureHeader.Nonce,SignatureHeader.Creator和ChannelHeader.Epoch。

希望大家关注我的微信公众号,有疑问可以后台留言。

链码实战(一)——Hello World 链码实战(一)——Hello World 一、概念 区块链2.0的典型代表就是智能合约,在Fabric中将其称为链码,智能合约具体是什么呢?好多人都有些蒙圈,在我看来智能合约对应到传统的互联网就是C/S中的S端部署的程序,因此基于区块链平台的开发,我可以理解成服务器开发。只不过这个开发是包裹上了区块链这一层技术。 智能合约的开发就是基于区块链的服务端应用开发。主要有以下几类 ... 阅读详情

相关推荐

hyperledger fabric 1.4 使用java开发智能合约

hyperledger fabric使用java开发智能合约,也较为简单,我们今天就来聊一聊如何使用java开发智能合约,如需完整的学习fabric,可以参考视频教程 首先需要在pom里面引入fabric的智能合约的jar包,如下图所示,需要注意的点如下,引入maven-shade-plugin包,设置mainclass,且finalname必须为chaincode。 <dependenci...

u013782473的博客 2822

fabric1.4 java sdk 中getStateByRange(““,““)无效

区块链链码

shinena_deng的博客 459

用Hyperledger Fabric和VSCode的IBM区块链扩展开发智能合约

了解使用IBM Blockchain Platform的VSCode扩展来简化开发,测试和部署智能合约的过程。完成本教程后,你将了解如何使用VSCode在本地Hyperledger Fabric网络上快速开发,演示和部署区块链应用程序。本教程假设你对Hyperledger Fabric有一些基本的了解。 学习目标 安装IBM Blockchain Platform VSCode扩展。 创建一个新...

mongo_node的专栏 2128

getstate java,getStateByRange()返回Hyperledger Fabric Java SDK中的空迭代器

I am trying to get all entries in the ledger (Fabric 1.4.4, using Java Chaincode):QueryResultsIterator iterator = ctx.getStub().getStateByRange("","");But I always get an empty iterator. Is this the c...

weixin_32098487的博客 423

超级账本HyperLedger:Fabric的Chaincode(智能合约、链码)开发、使用演示

作者: 李佶澳   转载请保留:原文地址   发布时间:2018/07/17 10:20:00 说明 Example-1:demo 合约安装&amp;单合约调用 获取合约代码 安装合约&amp;初始化 合约直接调用 并发或快速修改数据时需要注意的问题 合约链式调用 参考 说明 这是网易云课堂“IT技术快速入门学院”使用的素材。 这里演示合约的用法,合约...

IT技术快速入门学院的博客 6301

hyperledger fabric 1.4 智能合约api总结

智能合约开发以go语言为例,ChaincodeStubInterface为其智能合约的核心接口,本文主要讲解此接口的所有常用方法,如需视频学习,可以参考视频教程 stub.PutState(args[0],[]byte(args[1])) 用于将数据进行存储,采用K-V格式进行存储, stub.GetState(args[0]) 根据key获取value stub.DelState(k...

u013782473的博客 2405

使用Java开发Fabric智能合约Chaincode

场景:之前公司使用的是Fabric1.4,智能合约使用的是go语言开发的。现在为了方便接入到Bass平台,需要切换fabric的java SDK,并且智能合约也使用java开发。 1、pom文件 引入pom依赖: <dependency> <groupId>org.hyperledger.fabric-chaincode-java</groupId&g...

DamonREN的博客 8522

Hyperledger Fabric如何通过虚拟机部署以太坊智能合约

EVM作为用户链代码安装到Fabric中,然后可以通过它部署智能合约。单个EVM链代码足以在通道上运行多个以太坊智能合约。链码不采用以太坊的共识方法。所有事务仍将遵循Fabric事务流中的执行,订单,验证步骤。确保在不同组织中的足够对等方安装链代码,并设置一个确保一定程度的分散的认可政策。为了与已部署的智能合约进行交互,有一个fab3,它从以太坊JSON RPC API实现一组有限的API,因此可...

mongo_node的专栏 1724

Hyperledger Fabric Chaincode智能合约

前言 之前Hyperledger Fabric 1.1区块链的环境搭建完成了,接下来要为项目开发智能合约。那么先编写一份简单的智能合约吧。 什么是chaincode chaincode代码通常处理由网络成员同意的业务逻辑,因此它相当于“智能合约”。可以调用chaincode代码来更新或查询提案交易中的分类帐。考虑到适当的许可,chaincode代码可以调用另一个chaincode代码,无论是...

卿酒酒丶的博客 5867

第51篇 笔记-如何将自定义数据写到区块链中(合约方式)

本文环境: 区块链版本:以太坊POA联盟链 节点版本: geth 1.9.19 nodejs版本:v10.14.0 操作系统:windows 64 与上篇不同,本文提供另外一个方式,通过智能合约函数的方式把数据写到智能合约的变量区,其数据存储在storage区域,可通过函数查找出来。 1.智能合约 pragma solidity ^0.4.24; contract LongRecord{ mapping (uint => string) Record; ..

wonderBlock的博客 1720

Hyperledger Fabric使用Java自定义智能合约

文章目录Hyperledger Fabric使用Java自定义智能合约一、前提二、我的主要文件目录三、开始了1、启动测试网络2、创建通道3、配置环境变量4、合约代码5、打包合约源代码6、安装链码7、查看已安装的合约清单8、审批合约9、合约提交检查10、向通道提交合约11、测试拉四、最后 Hyperledger Fabric使用Java自定义智能合约

这里是Mae。 6182

Hyperledger Fabric 智能合约开发及 fabric-sdk-go/fabric-gateway 使用示例

在上个实验 Hyperledger Fabric 多组织多排序节点部署在多个主机上 中,我们已经实现了多组织多排序节点部署在多个主机上,但到目前为止,我们所有的实验都只是研究了联盟链的网络配置方法(尽管这确实是重难点),而没有考虑具体的应用开发。本文将在前面实验的基础上,首先尝试使用 Go 语言开发了一个工作室联盟链的项目信息智能合约,并成功将其部署至联盟链上;然后依据官方示例,使用 fabric-gateway 模块实现了一个能够管理项目信息智能合约的客户端;之后对比了 fabric-gateway 模块

风色幻想的博客 3万+

告别ROS1思维:在ROS2 Humble上,用Python搞定MoveIt2机械臂控制的完整流程

本文详细介绍了在ROS2 Humble环境下使用Python控制MoveIt2机械臂的完整流程。从环境配置、架构变革到实战操作,涵盖URDF适配、Action接口编程及性能优化技巧,帮助开发者高效迁移ROS1项目至ROS2平台,实现工业级机械臂控制。

weixin_30352645的博客 449

Hyperledger Fabric教程--部署Fabric智能合约

在HyperledgerFabric中,智能合约部署在称为链码的包中。想要验证交易或查询分类账的组织需要在其peer节点上安装链码。在加入通道的peer节点上安装链码后,通道成员可以将链码部署到通道,并使用链码中的智能合约在通道分类账上创建或更新资产。链码使用一个称为Fabric chaincode lifecycle的过程部署到通道。Fabric链码生命周期允许多个组织在链码可用之前就其操作方式达成一致。本教程学习如何使用peer lifecycle chaincode命令将链码部署到测试网络。

专注 AI、大数据、Python、爬虫、数据分析可视化、机器学习、推荐算法、知识图谱、大模型应用与全栈系统开发,长期沉淀真实项目案例、源码解析、系统设计、部署经验与工程化实践,分享可运行、可复用、可落地的技术方案。 5530

电机控制::软件架构::微型软件架构

所谓软件架构就是软件分层:HAL库,嵌入式操作系统,中间件,应用层。大型项目可参考借鉴ROS/PX4/Apollo/FMT等无人机、机器人、无人驾驶等开源项目;电机控制大型为AUTOSAR架构,小型跑裸机参考 前后台系统 或 微型实时操作系统对模块管理的思想。基本原理还是参照大型项目的架构来的,只是大部分层与块都相对简单,也没有操作系统这一层。硬件架构了解不同芯片组合及其各自的特点。1.FPGA+;2.架构;3.TC387+AUTOSAR架构,车规级,汽车领域常用。

one__leaf的博客 1695

大疆sdkV4以及V5webrtc推流

webrtcSDK中要用它的VideoCapturer来初始化推流 ViewdoCapturer是一个接口我们继承它 然后重建创建文件CapturerAircraft.java文件。CameraUtil.java这个是我们用来创建CapturerAircraft的一个工具。除了CapturerAircraft这个文件不一样外其他都是一样的。PlayBodyBean.java这个是我们的配置文件。下面的有个代码块第一个是v4的 第二个是v5的。接着我们来创建几个推流用到的webrtc工具。

weixin_41624865的博客 1187

Hyperledger Fabric 2.x 自定义智能合约

本文分享如何使用 Java 语言开发智能合约,以及合约的安装与使用。

zlt2000的博客 1万+

保姆级教程:不用看古早代码,5分钟搞懂泛微ecology9附件存储与接口调用

本文详细解析了泛微ecology9附件存储与接口调用的核心原理,提供从基础概念到实战操作的完整指南。通过拆解文档化附件设计理念、解析RESTful接口参数,并给出Python代码示例,帮助开发者快速掌握附件上传与业务集成的关键技巧,提升二次开发效率。

weixin_28688385的博客 349

如何使用 Java 语言为 Hyperledger Fabric 编写区块链链代码智能合约

面向 Java 开发人员的链代码简介 您或许听说过区块链,但可能不确定它对 Java™ 开发人员有何用。本教程将帮助大家解惑。我将分步展示如何使用 Hyperledger Fabric v0.6 来构建、运行和执行使用 Java 语言编写的智能合约或链代码。您将安装一些工具,定义本地区块链网络,构建并运行一个链代码智能合约。 有关区块链的概述,请参阅 developerWorks 博客中的 “...

mongo_node的专栏 8082

快速理解STLink引脚图中复位引脚作用

深入讲解STLink引脚图中复位引脚的实际用途,帮助开发者快速掌握其在调试与下载过程中的关键作用,结合stlink引脚图理解更高效。

weixin_32324637的博客 1325

USB PD Sink HUSB238A可应用于24V电动风扇/电动工具

HUSB238A在I2C模式下,可申请PD3.1 EPR AVS功率,可申请24V电压直接给电机供电

Hynetek的博客 759

一个小时带你用 Hyperledger Fabric开发一个Demo

先懂是什么,再懂怎么做,再懂为什么 本文作为系列开篇,力求通过一个简单fabric应用开发过程,让大家对fabric有个直观概括的了解。 前言 先放三张图: 第一张是Gartner 2017的技术成熟曲线图。对这张图的理解,大家仁者见仁智者见智,作为区块链从业者,既不想它被过度吹捧也不想被无脑黑。区块链不是万能的,但是在某些方向,尤其是对敏感信息加密共享有旺盛需求的领域,确实有其优势。 第二张是当前主要区块链项目的发展和应用状况。公共链领域,比特币依然一骑绝尘,以太坊紧跟...

建建的博客 4711

别再死磕RD算法了!用MATLAB手把手复现SAR后向投影(BP)成像(附完整代码与避坑点)

本文详细介绍了如何使用MATLAB实现合成孔径雷达(SAR)后向投影(BP)成像算法,突破传统RD算法在大斜视和复杂轨迹场景中的局限。通过完整的代码示例和避坑指南,帮助读者掌握BP算法的核心原理、内存优化策略和并行计算技巧,提升SAR成像精度与效率。

weixin_30908103的博客 296

(五)Fabric2.0 智能合约实践- 安装以及定义智能合约

总目录: (一)HyperLedger Fabric 2.0-release测试网络部署 (二)Fabric2.0 first-network 生成配置说明 (三)Fabric2.0启动网络脚本配置剖析 (四)Fabric2.0通道实践 目录1. fabric2.0合约新特性2 智能合约实践2.1 安装以及定义智能合约2.1.1 打包合约2.1.2 部署合约到节点2.1.3 当前组织同意合约定...

富强说 9364

基于DSIFT多尺度图像融合的MATLAB代码

多尺度图像融合是一种常用的图像处理技术,它可以将具有不同尺度和空间频率特征的图像融合成一幅更具信息丰富度和视觉效果的图像。DSIFT算法是一种基于SIFT算法的改进版本,它在提取图像特征时采用了密集采样的方法,能够有效地提取图像的局部特征。多尺度图像融合基于DSIFT算法的思想,通过在不同尺度下提取图像的DSIFT特征,并将这些特征进行融合,得到最终的融合图像。然后,通过DSIFT算法提取了两幅图像的特征。通过运行上述代码,我们可以在MATLAB中实现基于DSIFT多尺度图像融合的算法,并观察到融合结果。

IELLQUI9的博客 180

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

上一篇: hyperledger fabric 智能合约开发(二)------合约接口介绍
下一篇: hyperledger fabric 智能合约开发(四)------智能合约通用合约设计
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值