证书目录说明
ordererOrganization
peerOrganization-orgXXXX
--ca 组织根证书和私钥1
--tlsca tls根证书和私钥2
--msp
----admincerts 组织管理员身份验证证书3
----cacerts 组织根证书 就是上面的1
----tlscacerts tls根证书,就是上面的2
--peers
----peer0.orgXXXX
------msp
--------admincerts 组织管理员的身份验证证书,就是3
--------cacert 组织根证书,就是1
--------keystore 节点私钥
--------signcerts 组织根证书签名的本节点证书
--------tlscacerts 就是上面的2
------tls
--------ca.crt 组织tls根证书,就是上面的2
--------server.crt 组织根证书签名的本节点证书
--------server.key 本节点私钥
--users
----Admin@orgXXX
----msp
--------admincerts 组织管理员的身份验证证书,就是3
--------cacert 组织根证书,就是1
--------keystore 用户私钥
--------signcerts 组织根证书签名的本用户证书
--------tlscacerts 就是上面的2
----tls
------ca.crt
------server.crt
------server.key
----User1@orgXXX
------msp
--------admincerts 组织管理员的身份验证证书,就是3
--------cacert 组织根证书,就是1
--------keystore 用户私钥
--------signcerts 组织根证书签名的本用户证书
--------tlscacerts 就是上面的2
----tls
------ca.crt 就是上面的tls根证书2
------server.crt
------server.key
打开对接监控软件(statsd或者prometheus)开关
每个peer上
CORE_OPERATIONS_LISTENADDRESS=peer0.orgxxxxxxxxx:9443
CORE_METRICS_PROVIDER=prometheus
同时打开容器的9443端口映射
对应core.yaml配置,orderer节点也有core.yaml文件,不确定是否也能打开,待验证
https://www.zhihu.com/question/311029640
如何看待「男人四不娶(护士、幼师、银行女、女公务员)」这种说法?
https://www.linuxidc.com/Linux/2017-03/141593.htm
CentOS 7下Keepalived + HAProxy 搭建配置详解 HAProxy 的安装与配置
https://www.zhihu.com/question/54626462关于 jira confluence gitlab jenkins 的配置与整合以及常见的使用方式?
https://www.cnblogs.com/haoprogrammer/p/10245561.htmlkubernetes系列:(一)、kubeadm搭建kubernetes(v1.13.1)单节点集群
支持pvtdata,需要在channel的配置文件configtx.yaml中打开支持参考https://stackoverflow.com/questions/52987188/how-to-enable-private-data-in-fabric-v1-3-0Application: &ApplicationCapabilitiesV1_3: truefabric-samples的chaincode/marbles02_private/go/marbles_chaincode_private.go中首部的很多export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64)默认base64处理字符串后会在76字符后换行,关闭换行使用export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 -w 0)或者export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64 |td -d \\n)couchdb的web访问http://localhost:5984/_utils/
解释了区块一致性以及对背书的影响
https://lists.hyperledger.org/g/fabric/message/4896
https://www.jianshu.com/p/5e6cbdfe2657
样例代码直接访问账本区块文件,在1.4版本下编译成功但是运行报错
https://developer.ibm.com/cn/os-academy-hyperledger-fabric/
https://developer.ibm.com/cn/os-academy-hyperledger-fabric/
一个不错的ibm系列课程
---------------------------------------------------环境变量说明,和ubuntu环境搭建---------------------------------------------
docker镜像方式的多机集群环境节点角色和数量kafka,2f+1,3个节点zookeeper,3个节点orderer,1个或者多个节点peer,一个或者多个节点,和组织数量有关,至少一个组织,每个组织至少一个peer最分散布局,每个角色一个节点,最典型布局,3节点,和最小布局,1节点,测试用途分散布局 kafka节点 zookeeper节点 orderer节点 peer节点 client节点(可选)images kafka zookeeper orderer peer client couchdb javaenv ca(org内一个即可) tools(可选) ccenv baseos典型布局 节点1 节点2 节点3images kafka kafka kafka zookeeper zookeeper zookeeper orderer orderer(可选) orderer(可选) peer peer(可选) peer(可选) ca(org内一个)ca ca tools(可选) tools(可选) tools(可选) ccenv ccenv ccenv javaenv javaenv javaenv baseos baseos baseos couchdb couchdb couchdb 启动顺序1.zookeeper2.kafka3.couchdb4.ca5.orderer6.peer8.clikafka配置项 hostname: kafka0 enviroment: - KAFKA_MESSAGE_MAX_BYTES=103809024 # 99 * 1024 * 1024 B #表示单条消息体的最大大小 - KAFKA_REPLICA_FETCH_MAX_BYTES=103809024 # 99 * 1024 * 1024 B #从leader可以拉取的消息最大大小 - KAFKA_UNCLEAN_LEADER_ELECTION_ENABLE=false #是否从非ISR集合中选举follower副本称为新的leader - KAFKA_BROKER_ID=0 #Kafka集群中每个broker的唯一id值 - KAFKA_MIN_INSYNC_REPLICAS=2 #确认写成功的最小副本数,达到这个数目时才能确认最终写成功 #以上应该不会修改 - KAFKA_DEFAULT_REPLICATION_FACTOR=3 #副本数,应该指除leader外的replication数目 - KAFKA_ZOOKEEPER_CONNECT=zookeeper0:2181,zookeeper1:2181,zookeeper2:2181 #zookeeper连接参数,且与名字解析以及zookeeper的服务配置一致 ports: - 9092:9092 #将容器内端口映射到宿主机端口,冒号前宿主机端口,冒号后容器端口 extra_hosts: #可选,名字解析可以使用名字服务器模式,这里的名字是否和hostname以及zookeeper连接参数一致待定zookeeper配置项 hostname:zookeeper0 #这里的定义,必须和下方的服务定义,以及kafka节点中的zookeeper连接参数一致 environment: - ZOO_MY_ID=1 - ZOO_SERVERS=server.1=zookeeper0:2888:3888 server.2=zookeeper1:2888:3888 server.3=zookeeper2:2888:3888 #服务定义,这里的节点名字必须和hostname一致 ports: - 2181:2181 - 2888:2888 - 3888:3888orderer配置项 - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fabric_default #使用的docker network名称,若dockercompose启动,必须和默认名称或者事先创建的名称一致 #若其他方式启动,可选项有host(默认),bridge,ipvlan和none。就是docker容器的网络选择 - ORDERER_GENERAL_LOGLEVEL=warn - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 #绑定的ip地址 - ORDERER_GENERAL_LISTENPORT=7050 #绑定的port - ORDERER_GENERAL_GENESISMETHOD=file #创世区块形式,如果是provisional则指定Profile动态生成,如果是file则指定位置 - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block #创世区块文件位置 - ORDERER_GENERAL_LOCALMSPID=OrdererMSP #向msp manager注册local msp材料的id。这里的id必须和org定义的系统channel(/Channel/Orderer)配置的msp id一致 - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp #指定orderer所需的私有加密文件的位置 # enabled TLS - ORDERER_GENERAL_TLS_ENABLED=false #连接kafka时使用tls - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key #pem编码私钥,用作认证 - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt #公钥 - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt] #根证书,用来验证来自kafka集群的证书 #当channel创建,或者channel加载(比如orderer重启),orderer和kafka集群按照如下方式交互 # 为对应于该channel的kafka partition创建producer/writer # 使用该producer提交一个no-op CONNECT消息给那个partition # 为该partition创建一个consumer/reader #如果以上某个步骤失败,则在shorttotal期内每shortinterval重试一次, #且longtotal内每隔longinterval重复,直到成功为止 - ORDERER_KAFKA_RETRY_SHORTINTERVAL=1s - ORDERER_KAFKA_RETRY_SHORTTOTAL=30s - ORDERER_KAFKA_VERBOSE=true #打开和kafka的交互日志 # - ORDERER_KAFKA_RETRY_LONGINTERVAL=10s - ORDERER_KAFKA_RETRY_LONGTOTAL=100s - ORDERER_KAFKA_VERBOSE=true - ORDERER_KAFKA_BROKERS=[kafka0:9092,kafka1:9092,kafka2:9092,kafka3:9092] #kafka集群连接参数,服务器信息和名字服务器解析或者hosts文件一致 volumes: - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls ports: - 7050:7050couchdb配置项 environment: - COUCHDB_USER= - COUCHDB_PASSWORD= ports: - "5984:5984"ca配置项 environment: - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server #org的中间证书ICA目录,对应cryptogen生成目录的peerOrganizations/org1.example.com/ca/ - FABRIC_CA_SERVER_CA_NAME=ca - FABRIC_CA_SERVER_TLS_ENABLED=true - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem #tls公钥 - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/a272512e465ff74a214e6333916777912f08600a80a4597b4d9289e3b03231df_sk #tls私钥 ports: - "7054:7054"caikandapeer配置项(和core.yaml配置文件重复) environment: - CORE_PEER_ID=peer0.org1.example.com #peer在fabric网络中的唯一id - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 #peer在fabric网络中org内的的地址/服务端口,也是client连接的端点 - CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052 #peer在fabric网络中的链码地址/服务端口。这里未指定则使用下方的CORE_PEER_CHAINCODELISTENADDRESS,后者未指定则使用CORE_PEER_ADDRESS - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 #本地网络监听地址和端口,这里监听所有接口 - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 #peer在fabric网络中org之间的的地址/服务端口 - CORE_PEER_LOCALMSPID=Org1MSP #这里的设置必须符合本peer作为成员的每个channel的MSP名字,否则peer消息不会为其他节点识别 - CORE_LEDGER_STATE_STATEDATABASE=CouchDB #couchdb或者leveldb - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984 #如果选择couchdb,这里设置访问接口,名字为couchdb服务主机名,通过名字服务器或者hosts设置 - CORE_PEER_NETWORKID=fabric #网络的逻辑分隔 - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=e2e_default #使用的docker network名称,若dockercompose启动,必须和默认名称或者事先创建的名称一致 #若其他方式启动,可选项有host(默认),bridge,ipvlan和none。就是docker容器的网络选择 - CORE_LOGGING_LEVEL=DEBUG #日志级别 - CORE_PEER_GOSSIP_USELEADERELECTION=true #同一org内至少1个peer配置为true。使用动态算法选择leader,后者和orderer服务连接拉取账单区块 #本条和下一条不能同时为true,否则此时状态不确定。 - CORE_PEER_GOSSIP_ORGLEADER=false #静态指定leader - CORE_PEER_PROFILE_ENABLED=true #商用环境下必须关闭(false),Go语言的取样分析工具所用。 - CORE_PEER_TLS_ENABLED=true #要求server端tls - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt #tls服务器使用的x.509证书文件 - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key #tls服务器使用的私钥。如果clientAuthEnabled为true那么tls客户端私钥一并提供) - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt #根证书,CORE_PEER_TLS_KEY_FILE使用的证书链 volumes: - /var/run/:/host/var/run/ - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls ports: - 7051:7051 - 7052:7052 - 7053:7053--------------------------------------多机集群环境搭建过程物理机配置256gmem,10.42.120.237 root/Zchain1234$,使用kvm+qemu+libvirt虚机架构虚机配置2c8g20g,ubuntu1604,账户zchain/zchainzk0,zk1,zk2,kafka0,kafka1,kafka2,kafka3,orderer,peer0.org1,peer1.org1,peer0.org2,peer0.org2虚机地址使用dhcp(dnsmasq)方式,和mac地址关联地址稳定192.168.122.73 kafka0192.168.122.162 kafka1192.168.122.154 kafka2192.168.122.42 kafka3192.168.122.6 orderer.example.com192.168.122.106 peer0.org1.example.com 192.168.122.48 peer0.org2.example.com 192.168.122.59 peer1.org1.example.com 192.168.122.129 peer1.org2.example.com 192.168.122.181 zookeeper0192.168.122.100 zookeeper1192.168.122.217 zookeeper2对于每个虚机配置网络和docker环境,可以配置一个基准虚机,基于此进行复制生成不同的业务虚机,拉取不同的业务docker镜像以下是ubuntu1604的配置dns----cat /etc/resolv.conf10.30.1.10proxy----- ~/.bashrcexport http_proxy='http://proxyxa.example.com.cn:80'export https_proxy='http://proxyxa.example.com.cn:80/'export no_proxy="10.0.0.0/8,127.0.0.1,.example.com.cn"apt source-----------sources.list参考 http://mirrors.example.com.cn/help/#ubuntuapt proxy-----------Acquire::http::proxy "http://proxyxa.example.com.cn:80/";Acquire::https::proxy "https://proxyxa.example.com.cn:80/";openssh-server---------------apt install openssh-serverdocker-------sudo apt-get updatesudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-commoncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -sudo apt-key fingerprint 0EBFCD88sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"sudo apt-get updateapt-cache madison docker-cesudo apt-get install docker-ce=17.03.3~ce-0~ubuntu-xenialdocker-compose install--------------https://github.com/docker/compose/releasesdocker proxy-------------sudo mkdir -p /etc/systemd/system/docker.service.dsudo touch /etc/systemd/system/docker.service.d/http-proxy.confsudo vi /etc/systemd/system/docker.service.d/http-proxy.conf[Service]Environment="HTTP_PROXY=http://proxyxa.example.com.cn:80/" "HTTPS_PROXY=https://proxyxa.example.com.cn:80/"Environment="NO_PROXY=localhost,127.0.0.0/8,.example.com.cn,10.0.0.0/8"docker registry----------------sudo vi /etc/docker/daemon.json官网镜像{ "registry-mirrors": ["https://registry.docker-cn.com"]}内网私库{ "registry-mirrors": ["https://public-docker-virtual.artnj.example.com.cn"], "insecure-registries": ["0.0.0.0/0"]}sudo systemctl daemon-reloadsudo systemctl restart dockersystemctl show --property=Environment docker-------------------------------------------------------一个区块文件读取的例子,依赖configtxlator工具--------------------------------------------------
package main import ( "bufio" "bytes" "encoding/base64" "errors" "fmt" "io" "io/ioutil" "os" "os/exec" "github.com/golang/protobuf/proto" lutil "github.com/hyperledger/fabric/common/ledger/util" "github.com/hyperledger/fabric/protos/common" putil "github.com/hyperledger/fabric/protos/utils" ) var ErrUnexpectedEndOfBlockfile = errors.New("unexpected end of blockfile") var ( file *os.File fileName string fileSize int64 fileOffset int64 fileReader *bufio.Reader ) // Parse a block func handleBlock(block *common.Block) { fmt.Printf("Block: Number=[%d], CurrentBlockHash=[%s], PreviousBlockHash=[%s]\n", block.GetHeader().Number, base64.StdEncoding.EncodeToString(block.GetHeader().DataHash), base64.StdEncoding.EncodeToString(block.GetHeader().PreviousHash)) if putil.IsConfigBlock(block) { fmt.Printf(" txid=CONFIGBLOCK\n") } else { for _, txEnvBytes := range block.GetData().GetData() { if txid, err := extractTxID(txEnvBytes); err != nil { fmt.Printf("ERROR: Cannot extract txid, error=[%v]\n", err) return } else { fmt.Printf(" txid=%s\n", txid) } } } // write block to file b, err := proto.Marshal(block) if err != nil { fmt.Printf("ERROR: Cannot marshal block, error=[%v]\n", err) return } filename := fmt.Sprintf("block%d.block", block.GetHeader().Number) if err := ioutil.WriteFile(filename, b, 0644); err != nil { fmt.Printf("ERROR: Cannot write block to file:[%s], error=[%v]\n", filename, err) } strCommand := fmt.Sprintf("configtxlator proto_decode --input %s --type common.Block", filename) command := execCommand(strCommand) jsonFileName := fmt.Sprintf("block%d.json", block.GetHeader().Number) if err = ioutil.WriteFile(jsonFileName, []byte(command), 0644); err != nil { fmt.Printf("ERROR: Cannot write json string to file:[%s], error=[%v]\n", jsonFileName, err) } } func execCommand(strCommand string) string { cmd := exec.Command("/bin/bash", "-c", strCommand) var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { fmt.Println("Execute failed when run cmd:" + err.Error()) return "" } return out.String() } func nextBlockBytes() ([]byte, error) { var lenBytes []byte var err error // At the end of file if fileOffset == fileSize { return nil, nil } remainingBytes := fileSize - fileOffset peekBytes := 8 if remainingBytes < int64(peekBytes) { peekBytes = int(remainingBytes) } if lenBytes, err = fileReader.Peek(peekBytes); err != nil { return nil, err } length, n := proto.DecodeVarint(lenBytes) if n == 0 { return nil, fmt.Errorf("Error in decoding varint bytes [%#v]", lenBytes) } bytesExpected := int64(n) + int64(length) if bytesExpected > remainingBytes { return nil, ErrUnexpectedEndOfBlockfile } // skip the bytes representing the block size if _, err = fileReader.Discard(n); err != nil { return nil, err } blockBytes := make([]byte, length) if _, err = io.ReadAtLeast(fileReader, blockBytes, int(length)); err != nil { return nil, err } fileOffset += int64(n) + int64(length) return blockBytes, nil } func deserializeBlock(serializedBlockBytes []byte) (*common.Block, error) { block := &common.Block{} var err error b := lutil.NewBuffer(serializedBlockBytes) if block.Header, err = extractHeader(b); err != nil { return nil, err } if block.Data, err = extractData(b); err != nil { return nil, err } if block.Metadata, err = extractMetadata(b); err != nil { return nil, err } return block, nil } func extractHeader(buf *lutil.Buffer) (*common.BlockHeader, error) { header := &common.BlockHeader{} var err error if header.Number, err = buf.DecodeVarint(); err != nil { return nil, err } if header.DataHash, err = buf.DecodeRawBytes(false); err != nil { return nil, err } if header.PreviousHash, err = buf.DecodeRawBytes(false); err != nil { return nil, err } if len(header.PreviousHash) == 0 { header.PreviousHash = nil } return header, nil } func extractData(buf *lutil.Buffer) (*common.BlockData, error) { data := &common.BlockData{} var numItems uint64 var err error if numItems, err = buf.DecodeVarint(); err != nil { return nil, err } for i := uint64(0); i < numItems; i++ { var txEnvBytes []byte if txEnvBytes, err = buf.DecodeRawBytes(false); err != nil { return nil, err } data.Data = append(data.Data, txEnvBytes) } return data, nil } func extractMetadata(buf *lutil.Buffer) (*common.BlockMetadata, error) { metadata := &common.BlockMetadata{} var numItems uint64 var metadataEntry []byte var err error if numItems, err = buf.DecodeVarint(); err != nil { return nil, err } for i := uint64(0); i < numItems; i++ { if metadataEntry, err = buf.DecodeRawBytes(false); err != nil { return nil, err } metadata.Metadata = append(metadata.Metadata, metadataEntry) } return metadata, nil } func extractTxID(txEnvelopBytes []byte) (string, error) { txEnvelope, err := putil.GetEnvelopeFromBlock(txEnvelopBytes) if err != nil { return "", err } txPayload, err := putil.GetPayload(txEnvelope) if err != nil { return "", nil } chdr, err := putil.UnmarshalChannelHeader(txPayload.Header.ChannelHeader) if err != nil { return "", err } return chdr.TxId, nil } func input(message string) string { scanner := bufio.NewScanner(os.Stdin) fmt.Print(message) scanner.Scan() if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "error:", err) } return scanner.Text() } func main() { fileName := input("Please input block file name: ") var err error if file, err = os.OpenFile(fileName, os.O_RDONLY, 0600); err != nil { fmt.Printf("ERROR: Cannot Open file: [%s], error=[%v]\n", fileName, err) return } defer file.Close() if fileInfo, err := file.Stat(); err != nil { fmt.Printf("ERROR: Cannot Stat file: [%s], error=[%v]\n", fileName, err) return } else { fileOffset = 0 fileSize = fileInfo.Size() fileReader = bufio.NewReader(file) } execCommand("rm -rf ./block*.block ./block*.json") // Loop each block for { if blockBytes, err := nextBlockBytes(); err != nil { fmt.Printf("ERROR: Cannot read block file: [%s], error=[%v]\n", fileName, err) break } else if blockBytes == nil { // End of file break } else { if block, err := deserializeBlock(blockBytes); err != nil { fmt.Printf("ERROR: Cannot deserialize block from file: [%s], error=[%v]\n", fileName, err) break } else { handleBlock(block) } } } fmt.Println("\nParse block file to json files successfully, please check files named \"block*.json\" on current directory!") }
转载于:https://www.cnblogs.com/dablyo/p/10697459.html