一  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. NOIP提高组初战告捷

    前天第一次参加NOIP初赛,竟然提高组考了57分进入复赛啊啊!原本自己估分是52竟然估少了[滑稽]这个成绩 是我们学校初一提高组成绩最高 还是不错(出乎我意料之外)的!

  2. 第十二周助教工作总结——NWNU李泓毅

    助教博客链接:https://www.cnblogs.com/NWNU-LHY/ 本次作业的要求:基于原型的团队项目需求调研与分析:https://www.cnblogs.com/nwnu-daizh ...

  3. 时间控件My97DatePicker事件监听及用法

    引入js <script src="My97DatePicker/WdatePicker.js"></script> 应用 <input type=& ...

  4. Barman 安装

    Barman需要用到psql客户端,所以需要在Barman服务器安装psql数据库 1 linux操作系统环境准备: python版本:2.6或2.7 yum -y install epel-rele ...

  5. Are query string keys case sensitive?

    Are query string keys case sensitive? @gbjbaanb's answer is incorrect: The RFCs only specify the all ...

  6. 刷新指定窗口页面的gridTable数据

    top.frames[windowName].$("#gridTable").trigger("reloadGrid"); 其中”windowName即是窗口页 ...

  7. 创建WebApi Odata v3 终结点

    开放数据协议(OData) 是用于 web 的数据访问协议. OData 提供统一的方法来构造数据. 查询的数据和操作该数据集通过 CRUD 操作 (创建. 读取. 更新和删除). OData 支持 ...

  8. 深入Resource实现多语言支持的应用详解

    首先为假设有一个应用程序CAStudy,接着右键添加一个资源文件Resource1.resx. Resource1.resx里面如下: Main函数如下:static void Main(){    ...

  9. Tekla 导出ifc并浏览

    Tekla导出IFC

  10. ckpt pb

    (t20190518) luo@luo-All-Series:/dev/disk_2019/mask_rcnn_20190518/Mask_RCNN_20190902/models/research$ ...