Hyperledger Fabric 源码 浅谈

fabric错误集 1 [channel: assetschannel] Rejecting broadcast of config message from 172.26.0.7:56920 because of error: error authorizing update: error validating ReadSet: readset expected key [Group] /Channel/Applic... 阅读详情
  • 2021-09-18 更: 现在请忽略这篇博文吧,后续再补,fabric的每一块,基本都很清楚了
  • 2020-12-21
  • fabric-sdk-java,fabric-ca 都改完了,fabric改的差不多了,源码有空再补吧,现在留着的挺浅的:-)
  • 2020-07-22
    • 添加创建channel时候的相关debug

DEBUG源码流程所需:

进入 github.com/hyperledger/fabric/cmd/orderer
赋值orderer.yaml 到该目录
在orderer.yaml 中配置tls 相关信息,
再在主函数中设置
os.Setenv("FABRIC_CFG_PATH", "/Users/joker/go/src/github.com/hyperledger/fabric/cmd/orderer")
os.Setenv("FABRIC_LOGGING_SPEC", "DEBUG")
即可

Orderer

创建channel:

  • 路口处理方法为:
    // Handle reads requests from a Broadcast stream, processes them, and returns the responses to the stream
    func (bh *Handler) Handle(srv ab.AtomicBroadcast_BroadcastServer) error {
    	addr := util.ExtractRemoteAddress(srv.Context()) // 解析获取广播的ip地址
    	logger.Debugf("Starting new broadcast loop for %s", addr)
    	for {
    		msg, err := srv.Recv() // 接收节点发送过来的数据,cli 或者是peer
    		if err == io.EOF {
    			logger.Debugf("Received EOF from %s, hangup", addr)
    			return nil
    		}
    		if err != nil {
    			logger.Warningf("Error reading from %s: %s", addr, err)
    			return err
    		}
    		// 开始处理消息
    		resp := bh.ProcessMessage(msg, addr)
    		// 响应消息的结果
    		err = srv.Send(resp)
    		if resp.Status != cb.Status_SUCCESS {
    			return err
    		}
    
    		if err != nil {
    			logger.Warningf("Error sending to %s: %s", addr, err)
    			return err
    		}
    	}
    
    }
    
    • srv.Recv():广播并且接收节点发送过来的消息

    • func (ss *serverStream) RecvMsg(msg interface{}) error {
      err := ss.ServerStream.RecvMsg(msg)
      // 同时会有校验,校验的只是日志相关,因此不去细究
      if ce := ss.payloadLogger.Check(ss.payloadLevel, “received stream message”); ce != nil { //
      ce.Write(ProtoMessage(“message”, msg))
      }
      return err
      }

    • *处理消息:resp := bh.ProcessMessage(msg, addr)

      • 内部代码细究

        • 打点日志跟踪相关
      • func (bh *Handler) ProcessMessage(msg *cb.Envelope, addr string) (resp *ab.BroadcastResponse){
        tracker := &MetricsTracker{
        ChannelID: “unknown”,
        TxType: “unknown”,
        Metrics: bh.Metrics,
        }
        defer func() {
        // This looks a little unnecessary, but if done directly as
        // a defer, resp gets the (always nil) current state of resp
        // and not the return value
        tracker.Record(resp)
        }()
        tracker.BeginValidate()
        …
        }

        • **Handler 调用 ChannelSupportRegistrar **,

        • 内部调用如下:

        • chdr, isConfig, processor, err := bh.SupportRegistrar.BroadcastChannelSupport(msg)
            	if chdr != nil {
            		tracker.ChannelID = chdr.ChannelId
            		tracker.TxType = cb.HeaderType(chdr.Type).String()
            	}
            	if err != nil {
            		logger.Warningf("[channel: %s] Could not get message processor for serving %s: %s", tracker.ChannelID, addr, err)
            		return &ab.BroadcastResponse{Status: cb.Status_BAD_REQUEST, Info: err.Error()}
            	}
          
        • ChannelSupportRegistarar的声明:

        • // ChannelSupportRegistrar provides a way for the Handler to look up the Support for a channel
          type ChannelSupportRegistrar interface {
          // BroadcastChannelSupport returns the message channel header, whether the message is a config update
          // and the channel resources for a message or an error if the message is not a message which can
          // be processed directly (like CONFIG and ORDERER_TRANSACTION messages)
          BroadcastChannelSupport(msg *cb.Envelope) (*cb.ChannelHeader, bool, ChannelSupport, error)
          }

          如注释所言,提供的是对于channel 的适配性

        • 实际调用:

        • func (r *Registrar) BroadcastChannelSupport(msg *cb.Envelope) (*cb.ChannelHeader, bool, *ChainSupport, error) {
          	// 先解析 envelop 对象,并且简单校验
          	chdr, err := protoutil.ChannelHeader(msg)
          	if err != nil {
          		return nil, false, nil, fmt.Errorf("could not determine channel ID: %s", err)
          	}
           // registrar 对象内部有一个 	chains map[string]*ChainSupport的成员
           // 变量, 通过读写锁控制,因为刚开始创建channel,所以内部只有一个系统channel
           // Q : 什么时候写入: 
           // 	A: 
          	cs := r.GetChain(chdr.ChannelId)
          	// New channel creation
          	if cs == nil {
          	// 该channel(demochannel)不存在 ,则获取到系统channel
          		sysChan := r.SystemChannel()
          		if sysChan == nil {
          			return nil, false, nil, errors.New("channel creation request not allowed because the orderer system channel is not defined")
          		}
          		// 则用 系统channel 代替 currenChanel
          		cs = sysChan
          	}
          
          	isConfig := false
           // ClassifyMsg 是Processor 接口中的方法,该接口的存在类似于责任链
          	switch cs.ClassifyMsg(chdr) { // 通过envelop中的消息判断消息类型,有 配置更新msg 也有配置msg
          	case msgprocessor.ConfigUpdateMsg:
          	// 初次创建,需要更新配置,所以是 CONFIG_UPDATE 消息类型
          		isConfig = true
          	case msgprocessor.ConfigMsg:
          		return chdr, false, nil, errors.New("message is of type that cannot be processed directly")
          	default:
          	}
          
          	return chdr, isConfig, cs, nil
          }
          
        • 之后则判断是配置更新消息,还是普通的消息:

        • 配置更新消息:

        • else { // isConfig
          		logger.Debugf("[channel: %s] Broadcast is processing config update message from %s", chdr.ChannelId, addr)
          		 // 调用Processor 接口体系 ,处理 配置更新消息
          		config, configSeq, err := processor.ProcessConfigUpdateMsg(msg)
          		if err != nil {
          			logger.Warningf("[channel: %s] Rejecting broadcast of config message from %s because of error: %s", chdr.ChannelId, addr, err)
          			return &ab.BroadcastResponse{Status: ClassifyError(err), Info: err.Error()}
          		}
          		tracker.EndValidate()
          
          		tracker.BeginEnqueue()
          		if err = processor.WaitReady(); err != nil {
          			logger.Warningf("[channel: %s] Rejecting broadcast of message from %s with SERVICE_UNAVAILABLE: rejected by Consenter: %s", chdr.ChannelId, addr, err)
          			return &ab.BroadcastResponse{Status: cb.Status_SERVICE_UNAVAILABLE, Info: err.Error()}
          		}
          
          		err = processor.Configure(config, configSeq)
          		if err != nil {
          			logger.Warningf("[channel: %s] Rejecting broadcast of config message from %s with SERVICE_UNAVAILABLE: rejected by Configure: %s", chdr.ChannelId, addr, err)
          			return &ab.BroadcastResponse{Status: cb.Status_SERVICE_UNAVAILABLE, Info: err.Error()}
          		}
          	}
          
        • Processor#ProcessConfigUpdateMsg这个方法只用于配置更新CONFIG_UPDATE 或者是系统channel自身,实际调用如下:

        • func (s *SystemChannel) ProcessConfigUpdateMsg(envConfigUpdate *cb.Envelope) (config *cb.Envelope, configSeq uint64, err error) {
          	// 解析获取消息中的channelId
          	channelID, err := protoutil.ChannelID(envConfigUpdate)
          	// fabric 安全性的体现之一,名称是否要对应,内部调用的是
          	if channelID == s.support.ChannelID() {
          		return s.StandardChannel.ProcessConfigUpdateMsg(envConfigUpdate)
          	}
          
          	// 没进入上路的方法,就说明不是系统通道,那就是创建被,创建则意味着需要配置更新
          	// 内部方法与 configtx.yaml有关,
          	bundle, err := s.templator.NewChannelConfig(envConfigUpdate)
          	if err != nil {
          		return nil, 0, err
          	}
          
        • NewChannelConfig方法 如下,常见的问题都是出自这里:,什么version不对,policy错误等信息

          if configUpdate.WriteSet.Groups == nil || configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey] == nil {
          		return nil, fmt.Errorf("Config update has missing application group")
          	}
          
          	if uv := configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Version; uv != 1 {
          		return nil, fmt.Errorf("Config update for channel creation does not set application group version to 1, was %d", uv)
          	}
            // 对应configtxgen的consortium ,
          	consortiumConfigValue, ok := configUpdate.WriteSet.Values[channelconfig.ConsortiumKey]
          	.......
          	.......
          	.......
          	接下来就是一系列的初始化赋值操作
          .........
          .........
          .........
          	if oc, ok := dt.support.OrdererConfig(); ok && oc.Capabilities().PredictableChannelTemplate() {
          		channelGroup.ModPolicy = systemChannelGroup.ModPolicy
          		// 全部初始化版本号为0,在区块链中版本号,以及处理好都非常重要
          		zeroVersions(channelGroup)
          	}
          // 生成新的配置,内部有一块方法要注意:
          	bundle, err := channelconfig.NewBundle(channelHeader.ChannelId, &cb.Config{
          		ChannelGroup: channelGroup,
          	}, dt.bccsp)
          
          	if err != nil {
          		return nil, err
          	}
          
          	return bundle, nil
          
        • NewBundle

        • .....
          .....
          .....
          policyProviderMap := make(map[int32]policies.Provider)
          	for pType := range cb.Policy_PolicyType_name {
          		rtype := cb.Policy_PolicyType(pType)
          		switch rtype {
          		case cb.Policy_UNKNOWN:
          			// Do not register a handler
          		case cb.Policy_SIGNATURE:
          			policyProviderMap[pType] = cauthdsl.NewPolicyProvider(channelConfig.MSPManager())
          		case cb.Policy_MSP:
          			// Add hook for MSP Handler here
          		}
          	}
          map信息为:
          var Policy_PolicyType_name = map[int32]string{
          	0: "UNKNOWN",
          	1: "SIGNATURE",
          	2: "MSP",
          	3: "IMPLICIT_META",
          }
          
        • 之后则初始化manager信息,如

          • role信息

          • policy信息:

            • sign nOutOf 还是 SignBy
            • Admins,readers,writers等
          • policyManager, err := policies.NewManagerImpl(RootGroupKey, policyProviderMap, config.ChannelGroup)
            	if err != nil {
            		return nil, errors.Wrap(err, "initializing policymanager failed")
            	}
            
            
          • 再配置校验相关:

            • configtxManager, err := configtx.NewValidatorImpl(channelID, config, RootGroupKey, policyManager)
              	if err != nil {
              		return nil, errors.Wrap(err, "initializing configtx manager failed")
              	}
              
        • 接着就开始真正的处理消息了

        • 通过刚才生成的bundle
            	newChannelConfigEnv, err := bundle.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate)
            	if err != nil {
            		return nil, 0, errors.WithMessagef(err, "error validating channel creation transaction for new channel '%s', could not successfully apply update to template configuration", channelID)
            	}
          
        • ,交给内部的成员变量实现 ValidatorImpl

        • func (vi *ValidatorImpl) proposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
          // proto 反序列化
          	configUpdateEnv, err := protoutil.EnvelopeToConfigUpdate(configtx)
          	if err != nil {
          		return nil, errors.Errorf("error converting envelope to config update: %s", err)
          	}
           // 校验消费是否合理,大多数错误也是在这里爆出
          	configMap, err := vi.authorizeUpdate(configUpdateEnv)
          	if err != nil {
          		return nil, errors.Errorf("error authorizing update: %s", err)
          	}
          
        • vi.authorizeUpdate 会校验如下

          • 注意,每次发生更新都会使得版本号+1,所以会总是提示版本号不一致的问题

          • channelId是否一致

          • 读写集

          • func (vi *ValidatorImpl) authorizeUpdate(configUpdateEnv *cb.ConfigUpdateEnvelope) (map[string]comparable, error) {
            	if configUpdateEnv == nil {
            		return nil, errors.Errorf("cannot process nil ConfigUpdateEnvelope")
            	}
            
            	configUpdate, err := UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate)
            	if err != nil {
            		return nil, err
            	}
            
            	if configUpdate.ChannelId != vi.channelID {
            		return nil, errors.Errorf("ConfigUpdate for channel '%s' but envelope for channel '%s'", configUpdate.ChannelId, vi.channelID)
            	}
            
            	readSet, err := mapConfig(configUpdate.ReadSet, vi.namespace)
            	if err != nil {
            		return nil, errors.Wrapf(err, "error mapping ReadSet")
            	}
            	// 校验读
            	err = vi.verifyReadSet(readSet)
            	if err != nil {
            		return nil, errors.Wrapf(err, "error validating ReadSet")
            	}
             // 校验写
            	writeSet, err := mapConfig(configUpdate.WriteSet, vi.namespace)
            	if err != nil {
            		return nil, errors.Wrapf(err, "error mapping WriteSet")
            	}
            	
            	// 计算获取?
            	deltaSet := computeDeltaSet(readSet, writeSet)
            	signedData, err := protoutil.ConfigUpdateEnvelopeAsSignedData(configUpdateEnv)
            	if err != nil {
            		return nil, err
            	}
            
            // 通过上述集合校验签名数据 ,内部会调用 validateModPolicy,通过signedData
            // 获取到modPolicy ,判断是admin用户还是普通用户
            // 判断是否有/Channel/Application/ChannelCreationPolicy 这个policy
            	if err = vi.verifyDeltaSet(deltaSet, signedData); err != nil {
            		return nil, errors.Wrapf(err, "error validating DeltaSet")
            	}
            
            	fullProposedConfig := vi.computeUpdateResult(deltaSet)
            	if err := verifyFullProposedConfig(writeSet, fullProposedConfig); err != nil {
            		return nil, errors.Wrapf(err, "full config did not verify")
            	}
            
            	return fullProposedConfig, nil
            }
            
          • 上述校验通过之后,需要将map转为Config实体

            • channelGroup, err := configMapToConfig(configMap, vi.namespace)
              	if err != nil {
              		return nil, errors.Errorf("could not turn configMap back to channelGroup: %s", err)
              	}
              
          • 最后返回结果:,同时 sequence 会+1

            • return &cb.ConfigEnvelope{
              		Config: &cb.Config{
              			Sequence:     vi.sequence + 1,
              			ChannelGroup: channelGroup,
              		},
              		LastUpdate: configtx,
              	}, nil
              
          • 成功之后,则需要重新生成签名数据,返回给客户端

            	newChannelEnvConfig, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, channelID, s.support.Signer(), newChannelConfigEnv, msgVersion, epoch)
            	if err != nil {
            		return nil, 0, err
            	}
            
            	wrappedOrdererTransaction, err := protoutil.CreateSignedEnvelope(cb.HeaderType_ORDERER_TRANSACTION, s.support.ChannelID(), s.support.Signer(), newChannelEnvConfig, msgVersion, epoch)
            	if err != nil {
            		return nil, 0, err
            	}
            
          • 最后一步,准备好数据之后,不能将任意的数据回送回去,还需要进行过滤,防止恶意或者无效的包,client无法兼容:,内部是 Rule 接口

          • err = s.StandardChannel.filters.Apply(wrappedOrdererTransaction)
              	if err != nil {
              		return nil, 0, err
              	}
              
              	return wrappedOrdererTransaction, s.support.Sequence(), nil
            }
            
            • Rule接口定义如下

            • accept请求或者直接reject请求

              // Rule defines a filter function which accepts, rejects, or forwards (to the next rule) an Envelope
              type Rule interface {
              // Apply applies the rule to the given Envelope, either successfully or returns error
              Apply(message *ab.Envelope) error
              }

            • 默认装配有如下 filter:

              • MaxBytes: 防止transaction 过大
              • expiration 防止请求超时
              • SigFilter: 校验policy是否合法
              • systemChainFilter: systemChain添加相关信息
          • 当更新完消息所带来的配置变化之后,处理业务

          • tracker.BeginEnqueue()
              		// 默认是调用 一个空的实现
              		if err = processor.WaitReady(); err != nil {
              			logger.Warningf("[channel: %s] Rejecting broadcast of message from %s with SERVICE_UNAVAILABLE: rejected by Consenter: %s", chdr.ChannelId, addr, err)
              			return &ab.BroadcastResponse{Status: cb.Status_SERVICE_UNAVAILABLE, Info: err.Error()}
              		}
              		// 之后则是开始调用Confiure ,该方法的作用在于  ? 
              		err = processor.Configure(config, configSeq)
              		if err != nil {
              			logger.Warningf("[channel: %s] Rejecting broadcast of config message from %s with SERVICE_UNAVAILABLE: rejected by Configure: %s", chdr.ChannelId, addr, err)
              			return &ab.BroadcastResponse{Status: cb.Status_SERVICE_UNAVAILABLE, Info: err.Error()}
              		}
              
            
  • 处理完消息之后,发送返回值

    • 通过grpc

    • err = srv.Send(resp)
        		if resp.Status != cb.Status_SUCCESS {
        			return err
        		}
        
        		if err != nil {
        			logger.Warningf("Error sending to %s: %s", addr, err)
        			return err
        		}
      
  • envelop 校验阶段

    • Processor(msg处理器) -> Rule(msg)->Support(msp)
      • Rule:校验 是直接reject 还是 filter
        • SigFIlter:

      • StandardChannelSupport
        • 校验
  • envelop校验结束

实例化channel


疑问

  • Q: Registrar 中存储的channel信息,什么时候 增加,删除,修改?
    • A:

一些临时的东西

消息通讯

  • 消息通过 Processor这个接口处理

    • // Processor provides the methods necessary to classify and process any message which
      // arrives through the Broadcast interface.
      type Processor interface {
      	// 用于 检测 msg 的头部header信息,判断用哪个 消息处理器来处理消息 ,类似于责任链模式
      	ClassifyMsg(chdr *cb.ChannelHeader) Classification
      
      	
      	// 处理消息的同时返回消息的序列号,用来校验消息是否过期
      	ProcessNormalMsg(env *cb.Envelope) (configSeq uint64, err error)
      
      	// ProcessConfigUpdateMsg will attempt to apply the config update to the current configuration, and if successful
      	// return the resulting config message and the configSeq the config was computed from.  If the config update message
      	// is invalid, an error is returned.
      	// 处理配置更新信息,每个processor 自身都有一大堆配置,同样也会返回序列号,和新的配置信息
      	ProcessConfigUpdateMsg(env *cb.Envelope) (config *cb.Envelope, configSeq uint64, err error)
      
      	// ProcessConfigMsg takes message of type `ORDERER_TX` or `CONFIG`, unpack the ConfigUpdate envelope embedded
      	// in it, and call `ProcessConfigUpdateMsg` to produce new Config message of the same type as original message.
      	// This method is used to re-validate and reproduce config message, if it's deemed not to be valid anymore.
      	// 可以认为只是一个包装方法
      	ProcessConfigMsg(env *cb.Envelope) (*cb.Envelope, uint64, error)
      }
      
      
    • 关键的实现类:

      • SigFilter

        • // SigFilter stores the name of the policy to apply to deliver requests to
          // determine whether a client is authorized
          type SigFilter struct {
          	normalPolicyName      string
          	maintenancePolicyName string
          	support               SigFilterSupport
          }
          
        • 判断是否合法

          • 如判断 /Channel/Orderer/Writers. 或者
  • orderer 通过 Rule接口决定是否过滤这个msg 还是直接reject这个消息

    • // Rule defines a filter function which accepts, rejects, or forwards (to the next rule) an Envelope
      type Rule interface {
      	// Apply applies the rule to the given Envelope, either successfully or returns error
      	Apply(message *ab.Envelope) error
      }
      
hyperledger fabric 2.0(一)环境搭建以及测试网络部署 一、基础环境整理(可参照:https://blog.youkuaiyun.com/tianshuhao521/article/details/83892178 https://blog.youkuaiyun.com/tianshuhao521/article/details/83894142) 1、安装curl: # yum install curl 安装成功后查看版本: # curl --version ... 阅读详情

相关推荐

Hyperledger Fabric 区块链多机部署

目录 目录 Hyperledger Fabric 区块链多机部署 环境介绍 源码编译以及fabric 区块链网络部署环境准备 编译 fabric区块链相关可执行程序 编译 go 相关工具 编译 fabric基础环境 编译区块链服务相关工具 多机部署fabric网络solo共识 配置 fabric 网络用户拓扑关系 配置Orderer 节点的启动创世区块新建通道交易的相

songbin830的专栏 2万+

fabric 2.0, orderer

orderer The initialization procedure is simliar with peer’s. Load configuration Initialize loggging system Start operation system, http listening… Create gRPC server Create AtomicBroadcastServer, hoo...

Kingwel Blog 665

Centos7中HyperLedger Fabric的安装部署

1、Fabric源码安装 HyperLedger Fabric的源码托管在GitHub上,托管地址为https://github.com/hyperledger/fabric 工具编译需要用倒Go语言环境,源码目录会放在 $GOPATH路径下 可以通过Git命令下载源码,或者go get命令获取,这里使用go get命令 go get github.com/hyperledger/fabric ...

unsafe.Pointer 2748

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

呜呜呜好难哇

Algernon98的博客 959

Hyperledger系列(三) Fabric 源码和镜像下载

Hyperledger Fabric 源码和镜像下载在这之前需要下载安装Docker和Go环境,安装简单,不再介绍。Hyperledger Fabric的源码和镜像下载非常简单,只需要执行下面一个命令:curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0-rc1上面的命令,是执行下面的脚本https://github.com/hyperledger/fab...

maixia24的专栏 8511

Hyperledger Fabric/Fabric-samples 安装及使用(Mac)

1.清单 brew Git cUrl docker docker-compose GoLang 2.开始安装 2.1 Git mac自带git,也可自行安装:brew install git 2.2 curl brew install curl 2.3 docker brew install docker 2.4 docker-compose 无需操作,docker安装完成后,也含有了docker-compose 以上安装细节也可自行百度,网上都有^.^ 2.5 GoLang 1&

weixin_44681233的博客 1万+

Michael.W谈hyperledger Fabric第10期-手动搭建Fabric网络之启动网络与功能调试一

Michael.W谈hyperledger Fabric第10期-手动搭建Fabric网络之启动网络与功能调试一1 网络内容器启动顺序2 启动网络3 创建通道4 peer节点加入通道5 链码的安装5.1 直接安装5.2 打包安装(推荐)6 通过客户端切换节点 1 网络内容器启动顺序 前面已经详细地讲解了编写管理1个orderer节点容器、4个peer节点容器(组织org1中2个,组织org2中2个...

Revelation_of_Turing 1346

Hyperledger Fabric 1.0 安装和使用

注意:代码路径已更新,可以直接参考 https://github.com/yeasy/docker-compose-files/tree/master/hyperledger_fabric/。Hyperledger Fabric 1.0 版本整体 重新设计了架构,预计将于今年 3 月份发布。目前已经进入了 alpha 阶段,可以进行功能性验证。本文将介绍如何快速启动一个 Fabric 1.0 的网...

yeasy的专栏 2万+

hyperledger fabric 1.0环境搭建出错记录

启动 ./network_setup.sh up 错误:2017-09-12 14:17:49.952 UTC [grpc] Printf -> DEBU 003 grpc: addrConn.resetTransport failed to create client transport: connection error: desc = "transport: Error while dia cp: cannot stat 'build/docker/gotools/bin/protoc-gen-go

自由的大月 2万+

hyperledger fabric 源码调试(orderer)环境搭建教程

###前言 ​ 接触hyperledger fabric有一段时间了,看文档搭集群写链码都做过,但要深入理解还是通过源码比较直观,遂决定部署起来。同多机器部署一样,官文对源码调试的描述较少,网上有些前辈已经写过但少有明确每一步的原因,本系列文尝试依据个人理解来描述构建fabric源码调试环境的步骤,去掉docker这层障碍,并给出思考过程。 ​ 之前有写过一篇hyperledger fabric ...

beijihukk的博客 1733

Hyperledger Fabric环境搭建流程记录详解

Fabric环境搭建记录 : 什么是超级账本Fabric: 1、Fabric的本质: 与一般区块链技术的相同点:Fabric与其他区块链技术一样,都有一个账本。和以太坊相像,也允许使用智能合约。从本质上看,它是参与者共同管理交易的系统,是联盟链的典型应用。 2、与一般区块链技术的不同点: 具有多种节点类型。不同节点赋予了不同的功能,增加了交易处理效率。 拥有准入资格授权机制。要成为超级账本...

AxlWu的博客 2014

hyperledger fabric源码编译(手把手教编译fabric源码)

1、导言: 网上大部分文章都是介绍直接获取fabric相关镜像的教程,很少涉及到fabric源码编译或者是不全面,本文重点是介绍fabric源码编译,不涉及fabric网络搭建和网络配置等过程,已经编译环境搭建过程;旨在加深读者端fabric的认识,知道fabric镜像是怎么来的。 2、Fabric镜像: Fabric镜像从所属属性可以分为两大类,一类是hyperLedg...

草根工厂——技术虐我千百遍,我依然待你如初恋 6249

零基础玩转NILM:用nilmtk提取UKDALE电器数据的保姆级教程

本文为零基础用户提供了一份详细的NILM入门指南,重点讲解如何使用nilmtk工具包从UKDALE数据集中提取电器级用电数据。教程涵盖环境搭建、核心概念解析、数据提取实战及常见问题排查,帮助读者快速掌握非侵入式负荷监测的数据预处理关键步骤。

rgv234567的博客 499

hyperledger fabric 2.3.3 搭建教程

hyperledger fabric 2.3.3搭建教程一、hyperledger fabric 环境搭建1. 安装curl,Git,pip并更新1.1 安装pip并更新1.2 安装curl,Git2. Golang 安装配置3. 安装Docker4. 安装Docker Compose5. 下载fabric和fabric-ca6. 拉取最新docker镜像(hyperledger fabric 2.x需要)7. 运行测试 一、hyperledger fabric 环境搭建 在项目开始,先搭建hyperled

weixin_44062177的博客 2万+

用Arduino+ESP32复现TDOA定位:低成本实现厘米级精度的完整教程

本文提供了一套利用Arduino和ESP32平台,结合NRF24L01+模块与GPS/PPS时间同步方案,低成本复现高精度TDOA(到达时间差)定位系统的完整教程。教程详细阐述了从硬件选型、天线阵列设计、时间同步实现到核心算法与多径抑制的实战步骤,旨在帮助开发者和硬件爱好者以可控成本实现厘米级精度的定位演示系统。

weixin_29084111的博客 162

MobaXterm破解14个会话限制的保姆级教程(附Python环境配置指南)

本文提供了MobaXterm破解14个会话限制的详细教程,并深入讲解了如何配置稳定的Python环境以支持相关脚本运行。通过理解软件授权机制、安全实践步骤以及许可文件的应用,帮助用户解除会话数量限制,实现高效的远程服务器管理与自动化运维。

weixin_29211309的博客 1637

搭建Hyperledger Fabric环境 的详细步骤,超级详细

本教程是跟着 某硅谷 三年前的视频一点点实现的。但是,跟着教程走,会出现很多视频里面没有出现过的问题,本文着重讲解搭建过程碰到的问题及解决方案!!1、 安装docker (见我之前的教程)2、安装docker-compose3、安装Golang 4. 安装node.js5、安装Git最好先更新apt-get,否则可能会下载版本很旧的git,这是个好习惯,在下载任何包的时候,最好都更新一下工具下面的截图是我学习的教程里面的截图, 但是在我操作的过程中会总有些镜像拉取不下来! 原因:因为官方的sh文件存在版

目前专注区块链相关技术的学习与分享 1万+

基于ROS2 Humble与Livox Mid-360的双雷达点云融合实战解析

本文详细解析了基于ROS2 Humble与Livox Mid-360的双雷达点云融合实战方案。从环境搭建、驱动安装、双雷达配置、外参标定、时间同步到点云融合核心实现,全面介绍了技术要点与避坑指南,并提供了工程化部署与性能优化建议,助力开发者高效实现高精度点云合成应用。

ss78901的博客 1093

Hyperledger Fabric环境搭建(Linux)

Hyperledger Fabric手把手环境搭建,包括Go安装、Docker安装、Docker-Compose安装、Git安装、Fabric安装、运行Fabric-samples网络。

有一分热,发一分光。 6647

Hadoop实战:用MapReduce分析YouTube视频数据集(附完整代码)

本文提供了一个完整的Hadoop MapReduce实战教程,指导读者使用MapReduce框架分析YouTube视频数据集,统计不同视频类别的数量分布。教程涵盖从数据获取、预处理、HDFS操作到Java代码编写、作业提交及结果可视化的全流程,并附有完整代码,帮助开发者深入理解分布式计算思想。

lilh34434的博客 198

从频谱分析到硬件加速:用ZYNQ-7020+FFT IP核实现实时信号处理的完整方案

本文详细介绍了如何利用ZYNQ-7020和FFT IP核实现实时信号处理的完整方案,涵盖系统架构设计、硬件加速实现及性能优化。通过异构计算架构,该方案将1024点FFT处理延迟压缩至28μs,显著提升工业自动化和无线通信等领域的实时频谱分析效率。

weixin_33704234的博客 467

Hyperledger Fabric从源码分析交易

在上一章Hyperledger Fabric从源码分析区块结构中提到了区块的概念,并从源码角度对区块的结构进行了剖析,知道一个简单的区块包含了下面几个部分 BlockHeader, 区块头 BlockData,区块数据 BlockMetaData,区块元数据 上篇文章中已经对区块头,区块数据,区块元数据各个字段的内容进行了分析。我们知道BlockData字段是一个切片,存储的是一条条交易,但是并没有对交易具体的结构进行剖析。那么这篇文章就来分析一下一条交易里面会有哪些东西。 1. 先猜想一下一条交易会有

pigff的博客 1852

EB配置I2c - 实现与PF8200的读写操作

①,基于硬件实现IIC通信(硬件GPIO引脚配置为I2c复用引脚)②,基于软件模拟IO实现IIC通信(代码控制SDA和SCL两个IO引脚的电平来模拟IIC时序)本文章基于64pin的恩智浦S32K146型号的芯片作为主机,从机是NXP的供电芯片。在本次实验中,我们将以使用EB工具链来配置硬件IIC通信。1,EB配置2,代码编写3,测试验证话不多说,走你~写操作比较简单,只需要申明一个I2c配置结构体变量即可。

weixin_48498880的博客 3767

ubuntu 搭建hyperledger-fabric开发环境

1、GO环境(建议方式2,版本较新)。    方式1:sudo apt-get install golang          方式2:wget https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz           sudo tar -C /usr/local -xzf go1.9.linux-amd64.tar.gz方式...

Hi,你好 1万+

「UG/NX」Block UI 曲线收集器CurveCollector

曲线收集器块用于选择一条或多条曲线,包括通过选择意图连接的末端曲线链。曲线选择意图用于此块,但它不支持“交点处停止”、“跟随圆角”、“中间链”和“特征内链”等规则选项。

何曾参静谧的博客 1090

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

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

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

建建的博客 4711

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: TCP/IP相关
下一篇: SpringBoot 整合mybatis 和mybatis-plus
Coder_Joker
博客等级 码龄8年 39粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值