一  checkConfig  Before
    1.1  private static final TestConfig testConfig = TestConfig.getConfig();     
    这里加载一个配置文件(test路径/src/test/java/org/hyperledger/fabric/sdk/testutils.properties,文件不存在就加载代码中写死的默认配置),
    配置文件需要设置peer,orderer,ca,eventhub的地址,组织mspid,组织域名。解析配置文件,将信息加载到sampleOrgs中,如果与CA通信
启用了tls,需要在sampleOrg中(字段caProperties)保存与CA通信的tls证书位置(src/test/fixture/sdkintegration/e2e-2Orgs/$(FAB_CONFIG_GEN_VERS)/crypto-config/peerOrganizations/$(DNAME)/ca/ca.$(DNAME)-cert.pem),用于之后为组织创建CA客户端,
当前样例中与CA通信默认没有启用TLS。
    ****注意sampleOrgs是一个重要的变量,保存了所有的配置信息,并且之后加入组织的成员信息也保存到sampleOrgs中,sampleOrgs集合中一
个典型组织结构如下:
    

  1.2  为每个组织设置CA客户端
    从testConfig中获取到sampleOrgs到testSampleOrgs中,遍历每个组织并设置每个组织创建CA客户端,用户之后访问CA服务器。

 
二  setup Test
    setup为测试主要流程实现,包括创建user,admin,peerAdmin,为每个用户获取CA证书,创建channel,安装链码,实例化链码,设置事件,进行交易
几个部分。
    2.1  为每个组织创建用户,也保存在sampleOrgs中
    组织的用户信息与常规配置不同,在程序运行区间会有动态添加用户的需求,并且程序在运行时,需要保存用户的信息。样例中为了简单,将用户实例对象序列化到文件中,所以这里第一步先去文件中恢复用户信息到sampleOrg中。
    反序列化本地缓存的用户对象(/tmp/HFCSampletest)到sampleStore中
    sampleStore = new SampleStore(sampleStoreFile); 
    enrollUsersSetup(sampleStore); //This enrolls users with fabric ca and setups sample store to get users later.
    1) 先获取组织的CA客户端实例;
    2) 设置CA实例的加密套件,用于加解密和验证;
    3) 创建用户,并通过ca.enroll(user, secret) 向CA服务端申请证书。
          keypair = cryptoSuite.keyGen();   // generate ECDSA keys: signing and encryption keys,非对称加密生成私钥
          //url:ca地址; body:通过kvpair和user生成; 加上user和secret去申请证书,最后从response中解析出证书
          String responseBody = httpPost(url + HFCA_ENROLL, body, new UsernamePasswordCredentials(user, secret));  
          这里需要实例化三种用户。
          首先是admin,即登陆ca的管理员用户,默认账号密码"admin", "adminpw"。这个用户在ca服务器启动时就有,先尝试从用户存储文件中获取,没有的话直接从CA中获取keypair和证书即可。所有的用户抽象为sampleUser的结构,如下图,keypaire和证书保存在enrollment中。另外所有的用户都保存到sampleOrg中。

然后是普通用户,普通用户可以有多个,先尝试从序列化后的用户存储文件中获取,没有的话需要先从CA注册,再创建keypair,申请证书。最后保存到sampleOrg字段usermap中

sampleOrg sampleorg:  组织集合,包括orderer的地址(与orderer通信时使用的tls证书是启动CA后去申请的),peer地址,user,admin,peerAdmin
Orderers集合:                 tls证书和orderer地址
HFCCient client:             cryptoSuite加密解密组件,
                                         channel(需要通过tx配置,组织envelope,用peerAdmin签名后,用orderer对象原子广播生成channel),                                 
                                         userContext(对最后要发送的envelpe做签名,如在创建channel时就是用peerAdmin对象的密钥去做签名,那就设置为peerAdmin, 如果是发送一笔普通交易,则可以用普通user做签名)
channel fooChannel:     
 
runFabricTest
1  创建HFClient,一个client配一个channel实例
    设置client的加密组件,用于加密,解密,验证。
 
2   创建channel
     获取org1的实例sampleOrg
     创建channel实例,传入client和sampleOrg
     Channel fooChannel = constructChannel(FOO_CHANNEL_NAME, client, sampleOrg);
             1)  设置userContext   client.setUserContext(sampleOrg.getPeerAdmin());

 
             2)  将sampleOrg下所有的orderer地址,实例化orderer对象,做成orderers集合

 orderers.add(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName), ordererProperties));
            3)  选择集合中的第一个orderer去创建channel
                 先读取[channel name].tx配置文件到ChannelConfiguration channelConfiguration对象中
                 然后创建channel,依据channel创建策略去创建channel,这里只需要peerAdmin的签名,如果策略需要更多,则需要指定更多
                 public Channel newChannel(String name, Orderer orderer, ChannelConfiguration channelConfiguration,   byte[]... channelConfigurationSignatures)
                 Channel newChannel = client.newChannel(name, anOrderer, channelConfiguration, client.getChannelConfigurationSignature(channelConfiguration, sampleOrg.getPeerAdmin()));
                 newChannnel实现流程,[channelName].tx实际就是一个envelope结构体,先发序列化出来

反序列化payload

反序列化payload的header中的channelheader

校验下header的值,common.java中定义了type的值:public enum HeaderType,这里我们tx中应该为CONFIG_UPDATE(2),表示一笔更新channel配置的交易

接着反序列化payload的data域,configUpdateEnv

                 获取其中的configUpdate字段内容,发送
                 

     从中会重新构建一个envelope,做签名后,再用orderer对象发送(发送给orderer对象中保存的地址,使用其tls证书)
                private void sendUpdateChannel(byte[] configupdate, byte[][] signers, Orderer orderer)   这里就要创建channel了
                 1) 先构建一个交易上下文
                  TransactionContext transactionContext = getTransactionContext();
                  从中调用new TransactionContext(this, userContext, client.getCryptoSuite());返回交易上下文对象

      cryptoPrimititives: client.getCrytoSuite() 加密解密验证套件,   userContext(即2-1中定义,主要是使用其公私钥来给envelope做签名),channel当前的channel对象,identity身份认证信息

      2)构建上下文对象后,要构建envelope,会使用上下文对象来作签名。重要:上下文对象中包含client的usercontext,这里要区别开创建channel传进来的signer,signer是直接把序列化的证书byte存到了envelope中的payload中的data域(data域为一个configupdateenv结构,包含了signatures字段)。而上下文对象中的client的usercontext则是用来对envelope各个部分做签名(包括payload整体做签名(放到envelope的开始),payload的header,payload的data)
                猜测:signer可以自己设置任意用户,任意多个签名byte,即指定channel设置的一个策略???????

       2-1)创建configupdateenv,同上面的configUpdateEnvelope结构,这里重新构建信封结构,添加签名。
                 这里签名只有peerAdmin的签名。configupdateEnv将作为envelope payload结构中data域

              2-2)然后设置envelope payload结构的header域,header域包括channel header(包含type,是普通交易还是channel配置等等和交易txid),signatureheader(签名头)
               final ByteString sigHeaderByteString = getSignatureHeaderAsByteString(transactionContext);  //signatureheader由交易上下文生成
               final ChannelHeader payloadChannelHeader = ProtoUtils.createChannelHeader(HeaderType.CONFIG_UPDATE,
                        transactionContext.getTxID(), name, transactionContext.getEpoch(), transactionContext.getFabricTimestamp(), null, null);  //channel header
                final Header payloadHeader = Header.newBuilder().setChannelHeader(payloadChannelHeader.toByteString())
                        .setSignatureHeader(sigHeaderByteString).build();   //设置envelope payload的header

               2-3)设置envelope payload的data域
               final ByteString payloadByteString = Payload.newBuilder()
                        .setHeader(payloadHeader)
                        .setData(configUpdateEnvBuilder.build().toByteString())
                        .build().toByteString();
 
                2-4)设置整个envelope
                ByteString payloadSignature = transactionContext.signByteStrings(payloadByteString);
                Envelope payloadEnv = Envelope.newBuilder()
                        .setSignature(payloadSignature)
                        .setPayload(payloadByteString).build();
 
                2-5)根据orderer中设置的orderer地址和tls,使用原子广播发送信封
                BroadcastResponse trxResult = orderer.sendTransaction(payloadEnv);
    
                至此channel创建完毕,将创建的channel加入到去安居channels变量中
                后续参见End2endIT.java   844
                 // Set peer to not be all roles but eventing.
                1)    newChannel.joinPeer(peer, createPeerOptions().setPeerRoles(PeerRole.NO_EVENT_SOURCE));
                添加所有的peer到channel中,peer对象的属性在sampleOrg中存储
               

 
                2)    for (Orderer orderer : orderers) { //add remaining orderers if any.
                        newChannel.addOrderer(orderer);
                      }
                      添加所有的orderer到channel中
 
                3)   设置eventhub,其实是设置与eventhub服务端的连接,后面去交易的时候才去设置监听的事件
 
    3   回到End2endIT.java 206行,在创建channel后安装实例化链码
         sampleStore.saveChannel(fooChannel);   在sampleStore中本地序列化存储创建的channel对象
         
         然后runChannel     runChannel(client, fooChannel, true, sampleOrg, 0);        
         3-1) 注册了chaincode事件
 
         3-1) 第一步,先安装链码,所有的peer都要安装,安装链码只需要封装proposal发送给peers就可以了
                Collection<Peer> peers = channel.getPeers();
                numInstallProposal = numInstallProposal + peers.size();
                responses = client.sendInstallProposal(installProposalRequest, peers);     
 
         3-2) 实例化链码
                responses = channel.sendInstantiationProposal(instantiateProposalRequest, channel.getPeers());      //封装proposal并发送给peer
 
         3-3) 发送交易,实例化链码也当作一笔交易处理,需要进行orderer排序,commiter验证提交
          channel.sendTransaction(successful, createTransactionOptions() //Basically the default options but shows it's usage.
                    .userContext(client.getUserContext()) //could be a different user context. this is the default.
                    .shuffleOrders(false) // don't shuffle any orderers the default is true.
                    .orderers(channel.getOrderers()) // specify the orderers we want to try this transaction. Fails once all Orderers are tried.
        .nOfEvents(nOfEvents) // The events to signal the completion of the interest in the transaction
           ) 
           successful是所有交易提案的结果集合
           在sendTransaction中,封装一个envelope,不同于channel创建中的envelope,这个envelope中payload封装为交易提案response,response为
           多个背书节点的响应,这里拿出来一个,ed是所有背书的集合(读写集签名),proposalResponsePayload为提案结果payload,共同作为payload
    for (ProposalResponse sdkProposalResponse : proposalResponses) {
                ed.add(sdkProposalResponse.getProposalResponse().getEndorsement());
                if (proposal == null) {
                    proposal = sdkProposalResponse.getProposal();
                    proposalTransactionID = sdkProposalResponse.getTransactionID();
                    proposalResponsePayload = sdkProposalResponse.getProposalResponse().getPayload();
 
                }
            }
 
           Payload transactionPayload = transactionBuilder
                    .chaincodeProposal(proposal)
                    .endorsements(ed)
                    .proposalResponsePayload(proposalResponsePayload).build();
           Envelope transactionEnvelope = createTransactionEnvelope(transactionPayload, userContext)
           resp = orderer.sendTransaction(transactionEnvelope);    3118行,发送信封,这里会逐个orderer依次发送,哪个返回success,就break。
  4     执行chaincode
     client.setUserContext(sampleOrg.getUser(TESTUSER_1_NAME));   //重要:执行交易使用普通用户对envelope做签名即可
 
     ///////////////
     /// Send transaction proposal to all peers
   TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest();
   transactionProposalRequest.setChaincodeID(chaincodeID);
   transactionProposalRequest.setChaincodeLanguage(CHAIN_CODE_LANG);
   //transactionProposalRequest.setFcn("invoke");
   transactionProposalRequest.setFcn("move");
   transactionProposalRequest.setProposalWaitTime(testConfig.getProposalWaitTime());
   transactionProposalRequest.setArgs("a", "b", "100")
 
     掠过交易提案过程,这里获取到所有提案结果successful后,设置并发送envelope给orderer  634
        out("Sending chaincode transaction(move a,b,100) to orderer.");
        eturn channel.sendTransaction(successful).get(testConfig.getTransactionWaitTime(), TimeUnit.SECONDS);
    执行到sendTransaction channel.java 3079
    return sendTransaction(proposalResponses, createTransactionOptions().orderers(orderers).userContext(userContext));
    然后同实例化链码一样,3179行,封装envelope并发送给orderer
        public CompletableFuture<TransactionEvent> sendTransaction(Collection<ProposalResponse> proposalResponses,TransactionOptions transactionOptions) 

hyperledger fabric超级账本java sdk样例e2e代码流程分析的更多相关文章

  1. 用Java为Hyperledger Fabric(超级账本)开发区块链智能合约链代码之部署与运行示例代码

    部署并运行 Java 链代码示例 您已经定义并启动了本地区块链网络,而且已构建 Java shim 客户端 JAR 并安装到本地 Maven 存储库中,现在已准备好在之前下载的 Hyperledger ...

  2. 用Java为Hyperledger Fabric(超级账本)编写区块链智能合约链代码

    编写第一个 Java 链代码程序 在上一节中,您已经熟悉了如何构建.运行.部署和调用链代码,但尚未编写任何 Java 代码. 在本节中,将会使用 Eclipse IDE.一个用于 Eclipse 的 ...

  3. 用Hyperledger Fabric(超级账本)来构建Java语言开发区块链的环境

    面向 Java 开发人员的链代码简介 您或许听说过区块链,但可能不确定它对 Java™ 开发人员有何用.本教程将帮助大家解惑.我将分步展示如何使用 Hyperledger Fabric v0.6 来构 ...

  4. Hyperledger Fabric 实战(十): Fabric node SDK 样例 - 投票DAPP

    Fabric node SDK 样例 - 投票DAPP 参考 fabric-samples 下的 fabcar 加以实现 目录结构 . ├── app │ ├── controllers │ │ └─ ...

  5. 区块链之Hyperledger(超级账本)Fabric v1.0 的环境搭建(更新)

    参考链接:https://blog.csdn.net/so5418418/article/details/78355868   https://blog.csdn.net/wgh1015398431/ ...

  6. Hyperledger Fabric Ledger——账本总账

    Ledger Ledger(账本)即所有的state transitions(状态切换),是有序且不可篡改的.state transitions(状态切换)是由参与方提交的chaincode(智能合约 ...

  7. Hyperledger Fabric 2.x Java区块链应用

    一.说明 在上一篇文章中 <Hyperledger Fabric 2.x 自定义智能合约> 分享了智能合约的安装并使用 cli 客户端进行合约的调用:本文将使用 Java 代码基于 fab ...

  8. java多线程样例

    这里我们做一个完整的样例来说明线程产生的方式不同而生成的线程的差别: package debug; import java.io.*;import java.lang.Thread; class My ...

  9. HTTP基本认证(Basic Authentication)的JAVA演示样例

    大家在登录站点的时候.大部分时候是通过一个表单提交登录信息.可是有时候浏览器会弹出一个登录验证的对话框.例如以下图,这就是使用HTTP基本认证.以下来看看一看这个认证的工作过程:第一步:  clien ...

随机推荐

  1. mysql排序字段为空的排在最后面

    排序字段为orderid; 1.使用order by orderid desc实现降序时,orderid 为null数据的会排在数据的最后面: 但是,order by orderid升序时,order ...

  2. Authentication failure. Retrying - 彻底解决vagrant up时警告

    碰到的问题 使用vagrant启动虚拟机时,出现如下警告: vagrant up default: Warning: Authentication failure. Retrying... 原因分析 ...

  3. 【转】adb server is out of date. killing完美解决

    今天,久未出现的著名的“adb server is out of date.  killing”又发生了,在此,将解决方法记下,以便日后查看. 1. 错误信息: C:\Users\lizy>ad ...

  4. MQTT教學(二):安裝MQTT伺服器Mosquitto,Windows系統篇

    http://swf.com.tw/?p=1005 「認識MQTT」文章提到,MQTT的訊息全都透過稱為代理人(broker)的伺服器交流.本文將說明頗受歡迎的開放原始碼MQTT伺服器Mosquitt ...

  5. EditText限制输入的几种方式及只显示中文汉字的做法

    最近项目要求限制密码输入的字符类型, 例如不能输入中文.   现在总结一下EditText的各种实现方式,  以比较各种方法的优劣. 第一种方式:  设置EditText的inputType属性,可以 ...

  6. springboot配置Filter的两种方法

    一.使用注解1. 假设Filter类的路径为com.sanro.filter @Slf4j @WebFilter(filterName = "authFilter", urlPat ...

  7. linux都有哪些运行级别?

    答: 一共有七种运行级别,如下: 0 – System halt i.e the system can be safely powered off with no activity. 1 – Sing ...

  8. Centos7搭建OpenNebula云平台

    OpenNebula概述 OpenNebula是专门为云计算打造的开源系统,用户可以使用Xen.KVM.VMware等虚拟化软件一起打造企业云.利用OpenNebula可以轻松构建私有云.混合云.公开 ...

  9. openresty开发系列29--openresty中发起http请求

    openresty开发系列29--openresty中发起http请求 有些场景是需要nginx在进行请求转发 用户浏览器请求url访问到nginx服务器,但此请求业务需要再次请求其他业务:如用户请求 ...

  10. 从0开始学爬虫2之json的介绍和使用

    从0开始学爬虫2之json的介绍和使用 Json 一种轻量级的数据交换格式,通用,跨平台 键值对的集合,值的有序列表 类似于python中的dict Json中的键值如果是字符串一定要用双引号 jso ...