四、使用Fabric-SDK-Go调用链码
在Org1的peer上使用SDK
1.安装Fabric-SDK-GO
go get github.com/hyperledger/fabric-sdk-go
2.复制Org2的tls证书
mkdir -p ~/work/example/organizations/peerOrganizations/org2.example.com/tlsca
cd ~/work/example/organizations/peerOrganizations/org2.example.com/
scp dev1@192.168.1.108:/home/dev1/work/example/organizations/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem tlsca/
3.创建配置文件
cd ~/work/example/sdk
vim connection-profile.yaml
#
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
#
version: 1.0.0
#
# The client section used by GO SDK.
#
client:
# Which organization does this application instance belong to? The value must be the name of an org
# defined under "organizations"
organization: Org1MSP
logging:
level: info
# Global configuration for peer, event service and orderer timeouts
# if this this section is omitted, then default values will be used (same values as below)
# peer:
# timeout:
# connection: 10s
# response: 180s
# discovery:
# # Expiry period for discovery service greylist filter
# # The channel client will greylist peers that are found to be offline
# # to prevent re-selecting them in subsequent retries.
# # This interval will define how long a peer is greylisted
# greylistExpiry: 10s
# eventService:
# the below timeouts are commented out to use the default values that are found in
# "pkg/fab/endpointconfig.go"
# the client is free to override the default values by uncommenting and resetting
# the values as they see fit in their config file
# timeout:
# registrationResponse: 15s
# orderer:
# timeout:
# connection: 15s
# response: 15s
# global:
# timeout:
# query: 180s
# execute: 180s
# resmgmt: 180s
# cache:
# connectionIdle: 30s
# eventServiceIdle: 2m
# channelConfig: 30m
# channelMembership: 30s
# discovery: 10s
# selection: 10m
# Root of the MSP directories with keys and certs.
cryptoconfig:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}
path: ../organizations
# Some SDKs support pluggable KV stores, the properties under "credentialStore"
# are implementation specific
credentialStore:
# [Optional]. Used by user store. Not needed if all credentials are embedded in configuration
# and enrollments are performed elswhere.
path: "./tmp/state-store"
# [Optional]. Specific to the CryptoSuite implementation used by GO SDK. Software-based implementations
# requiring a key store. PKCS#11 based implementations does not.
cryptoStore:
# Specific to the underlying KeyValueStore that backs the crypto key store.
path: ../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/
# [Optional] BCCSP config for the client. Used by GO SDK.
BCCSP:
security:
enabled: true
default:
provider: "SW"
hashAlgorithm: "SHA2"
softVerify: true
level: 256
tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemCertPool: true
# [Optional]. Client key and cert for TLS handshake with peers and orderers
client:
key:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
path: ../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/client.key
cert:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
path: ../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/client.crt
#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
#
channels:
# Default channel is used if channel configuration is missing or if defined channel configuration is missing info
# If channel doesn't define peers then peers from default channel will be used
# If channel doesn't define orderes then orderes from default channel will be used
# If channel doesn't define policies then policies from default channel will be used.
# Also, if channel defines policies and some policy info is missing than that missing info will be filled from default channel.
_default:
# Optional. list of peers from participating orgs
peers:
peer0.org1.example.com:
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
# have the chaincode installed. The app can also use this property to decide which peers
# to send the chaincode install request. Default: true
endorsingPeer: true
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
# installed. The app can also use this property to decide which peers to send the
# chaincode install request. Default: true
chaincodeQuery: true
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
# queryBlock(), queryTransaction(), etc. Default: true
ledgerQuery: true
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
# produce events but the app typically only needs to connect to one to listen to events.
# Default: true
eventSource: true
# [Optional]. The application can use these options to perform channel operations like retrieving channel
# config etc.
policies:
#[Optional] options for retrieving channel configuration blocks
queryChannelConfig:
#[Optional] min number of success responses (from targets/peers)
minResponses: 1
#[Optional] channel config will be retrieved for these number of random targets
maxTargets: 1
#[Optional] retry options for query config block
retryOpts:
#[Optional] number of retry attempts
attempts: 5
#[Optional] the back off interval for the first retry attempt
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
backoffFactor: 2.0
#[Optional] options for retrieving discovery info
discovery:
#[Optional] discovery info will be retrieved for these number of random targets
maxTargets: 2
#[Optional] retry options for retrieving discovery info
retryOpts:
#[Optional] number of retry attempts
attempts: 4
#[Optional] the back off interval for the first retry attempt
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
backoffFactor: 2.0
#[Optional] options for the event service
eventService:
# [Optional] resolverStrategy specifies the peer resolver strategy to use when connecting to a peer
# Possible values: [PreferOrg (default), MinBlockHeight, Balanced]
#
# PreferOrg:
# Determines which peers are suitable based on block height lag threshold, although will prefer the peers in the
# current org (as long as their block height is above a configured threshold). If none of the peers from the current org
# are suitable then a peer from another org is chosen.
# MinBlockHeight:
# Chooses the best peer according to a block height lag threshold. The maximum block height of all peers is
# determined and the peers whose block heights are under the maximum height but above a provided "lag" threshold are load
# balanced. The other peers are not considered.
# Balanced:
# Chooses peers using the configured balancer.
resolverStrategy: PreferOrg
# [Optional] balancer is the balancer to use when choosing a peer to connect to
# Possible values: [Random (default), RoundRobin]
balancer: Random
# [Optional] blockHeightLagThreshold sets the block height lag threshold. This value is used for choosing a peer
# to connect to. If a peer is lagging behind the most up-to-date peer by more than the given number of
# blocks then it will be excluded from selection.
# Note that this parameter is applicable only when minBlockHeightResolverMode is set to ResolveByThreshold.
# Default: 5
blockHeightLagThreshold: 5
# [Optional] reconnectBlockHeightLagThreshold - the event client will disconnect from the peer if the peer's
# block height falls behind the specified number of blocks and will reconnect to a better performing peer.
# Note that this parameter is only applicable if peerMonitor is set to Enabled (default).
# Default: 10
# NOTES:
# - Setting this value too low may cause the event client to disconnect/reconnect too frequently, thereby
# affecting performance.
reconnectBlockHeightLagThreshold: 8
# [Optional] peerMonitorPeriod is the period in which the connected peer is monitored to see if
# the event client should disconnect from it and reconnect to another peer.
# Default: 0 (disabled) for Balanced resolverStrategy; 5s for PreferOrg and MinBlockHeight strategy
peerMonitorPeriod: 6s
#[Required if _default not defined; Optional if _default defined].
# name of the channel
# mychannel:
channel1:
# list of orderers designated by the application to use for transactions on this
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
# operational decisions to share loads from applications among the orderers. The values must
# be "names" of orgs defined under "organizations/peers"
# deprecated: not recommended, to override any orderer configuration items, entity matchers should be used.
# orderers:
# - orderer.example.com
#[Required if _default peers not defined; Optional if _default peers defined].
# list of peers from participating orgs
peers:
peer0.org1.example.com:
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
# have the chaincode installed. The app can also use this property to decide which peers
# to send the chaincode install request. Default: true
endorsingPeer: true
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
# installed. The app can also use this property to decide which peers to send the
# chaincode install request. Default: true
chaincodeQuery: true
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
# queryBlock(), queryTransaction(), etc. Default: true
ledgerQuery: true
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
# produce events but the app typically only needs to connect to one to listen to events.
# Default: true
eventSource: true
#
# list of participating organizations in this network
#
organizations:
# Org1:
Org1MSP:
mspid: Org1MSP
# This org's MSP store (absolute path or relative to client.cryptoconfig)
# cryptoPath: peerOrganizations/org1.example.com/users/User1@org1.example.com/msp
cryptoPath: ../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
peers:
- peer0.org1.example.com
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
# network. Typically certificates provisioning is done in a separate process outside of the
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
# Fabric-CA servers.
# certificateAuthorities:
# - ca.org1.example.com
# the profile will contain public information about organizations other than the one it belongs to.
# These are necessary information to make transaction lifecycles work, including MSP IDs and
# peers with a public URL to send transaction proposals. The file will not contain private
# information reserved for members of the organization, such as admin key and certificate,
# fabric-ca registrar enroll ID and secret, etc.
# Org2:
# mspid: Org2MSP
# # This org's MSP store (absolute path or relative to client.cryptoconfig)
# cryptoPath: peerOrganizations/org2.example.com/users/{username}@org2.example.com/msp
# peers:
# - peer0.org2.example.com
# certificateAuthorities:
# - ca.org2.example.com
# Orderer Org name
# ordererorg:
# OrdererOrg:
# # Membership Service Provider ID for this organization
# mspID: OrdererMSP
# # Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode)
# cryptoPath: ordererOrganizations/example.com/users/{username}@example.com/msp
# #
# List of orderers to send transaction and channel create/update requests to. For the time
# being only one orderer is needed. If more than one is defined, which one get used by the
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
#
orderers: #这部分内容没有起作用,原因未知
orderer.example.com:
# [Optional] Default: Infer from hostname
url: orderer.example.com:7050
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
grpcOptions:
ssl-target-name-override: orderer.example.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
path: ../organizations/orderer.example.com/tlscacerts/tlsca.example.com-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
peer0.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
url: peer0.org1.example.com:7051
grpcOptions:
ssl-target-name-override: peer0.org1.example.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
path: ../organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
# peer1.org1.example.com:
# # this URL is used to send endorsement and query requests
# url: peer1.org1.example.com:7151
# grpcOptions:
# ssl-target-name-override: peer1.org1.example.com
# # These parameters should be set in coordination with the keepalive policy on the server,
# # as incompatible settings can result in closing of connection.
# # When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
# keep-alive-time: 0s
# keep-alive-timeout: 20s
# keep-alive-permit: false
# fail-fast: false
# # allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
# allow-insecure: false
# tlsCACerts:
# # Certificate location absolute path
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer0.org2.example.com:
url: peer0.org2.example.com:7051
grpcOptions:
ssl-target-name-override: peer0.org2.example.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
path: ../organizations/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
#
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
# certificate management to be done via REST APIs. Application may choose to use a standard
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
#
# certificateAuthorities:
# ca.org1.example.com:
# # [Optional] Default: Infer from hostname
# url: https://ca.org1.example.com:7054
# tlsCACerts:
# # Comma-Separated list of paths
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
# # Client key and cert for SSL handshake with Fabric CA
# client:
# key:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# # needed to enroll and invoke new users.
# registrar:
# enrollId: admin
# enrollSecret: adminpw
# # [Optional] The optional name of the CA.
# caName: ca.org1.example.com
# ca.org2.example.com:
# url: https://ca.org2.example.com:8054
# tlsCACerts:
# # Comma-Separated list of paths
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
# # Client key and cert for SSL handshake with Fabric CA
# client:
# key:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# # needed to enroll and invoke new users.
# registrar:
# enrollId: admin
# enrollSecret: adminpw
# # [Optional] The optional name of the CA.
# caName: ca.org2.example.com
# EntityMatchers enable substitution of network hostnames with static configurations
# so that properties can be mapped. Regex can be used for this purpose
# UrlSubstitutionExp can be empty which means the same network hostname will be used
# UrlSubstitutionExp can be given same as mapped peer url, so that mapped peer url can be used
# UrlSubstitutionExp can have golang regex matchers like ${1}.local.example.${2}:${3} for pattern
# like peer0.org1.example.com:1234 which converts peer0.org1.example.com to peer0.org1.local.example.com:1234
# sslTargetOverrideUrlSubstitutionExp follow in the same lines as
# SubstitutionExp for the fields gprcOptions.ssl-target-name-override respectively
# In any case mappedHost's config will be used, so mapped host cannot be empty, if entityMatchers are used
#entityMatchers:
entityMatchers:
peer:
- pattern: (\w+).org1.example.com:(\d+)
urlSubstitutionExp: ${1}.org1.example.com:${2}
sslTargetOverrideUrlSubstitutionExp: ${1}.org1.example.com
mappedHost: peer0.org1.example.com
- pattern: (\w+).org2.example.com:(\d+)
urlSubstitutionExp: ${1}.org2.example.com:${2}
sslTargetOverrideUrlSubstitutionExp: ${1}.org2.example.com
mappedHost: peer0.org2.example.com
orderer:
- pattern: (\w+) #理论上该写(\w+).example.(\w+),但总是访问127.0.0.1:7050,暂时未找到原因,先模糊匹配
urlSubstitutionExp: orderer.example.com:7050
sslTargetOverrideUrlSubstitutionExp: orderer.example.com
mappedHost: orderer.example.com
#
# - pattern: (\w+).example2.(\w+)
# urlSubstitutionExp: localhost:7050
# sslTargetOverrideUrlSubstitutionExp: localhost
# mappedHost: orderer.example.com
#
# - pattern: (\w+).example3.(\w+)
# urlSubstitutionExp:
# sslTargetOverrideUrlSubstitutionExp:
# mappedHost: orderer.example.com
#
# - pattern: (\w+).example4.(\w+):(\d+)
# urlSubstitutionExp: ${1}.example.${2}:${3}
# sslTargetOverrideUrlSubstitutionExp: ${1}.example.${2}
# mappedHost: orderer.example.com
#
# certificateAuthority:
# - pattern: (\w+).org1.example.(\w+)
# urlSubstitutionExp:
# mappedHost: ca.org1.example.com
#
# - pattern: (\w+).org2.example.(\w+)
# urlSubstitutionExp:
# mappedHost: ca.org2.example.com
4.编写sdk调用代码
vim main.go
package main
import (
"fmt"
"os"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/hyperledger/fabric-sdk-go/pkg/common/logging"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)
var (
cc = "sacc"
user = "Admin" //此处Admin,但实际中应使用User1
secret = ""
channelName = "channel1"
lvl = logging.INFO
orgName = "Org1MSP"
)
func main(){
c := config.FromFile("./connection-profile.yaml")
sdk, err := fabsdk.New(c)
if err != nil {
fmt.Printf("Failed to create new SDK: %s\n", err)
os.Exit(1)
}
defer sdk.Close()
clientChannelContext := sdk.ChannelContext(channelName, fabsdk.WithUser(user), fabsdk.WithOrg(orgName))
if err != nil {
fmt.Printf("Failed to create channel [%s] client: %#v", channelName, err)
os.Exit(1)
}
client, err := channel.New(clientChannelContext)
if err != nil {
fmt.Printf("Failed to create channel [%s]:", channelName, err)
}
queryCC(client, []byte("a"))
invokeCC(client, "ff55")
queryCC(client, []byte("a"))
}
func invokeCC(client *channel.Client, newValue string) {
invokeArgs := [][]byte{[]byte("a"), []byte(newValue)}
_, err := client.Execute(channel.Request{
ChaincodeID: cc,
Fcn: "set",
Args: invokeArgs,
})
if err != nil {
fmt.Printf("Failed to invoke: %+v\n", err)
}
}
func queryCC(client *channel.Client, name []byte) string {
var queryArgs = [][]byte{name}
response, err := client.Query(channel.Request{
ChaincodeID: cc,
Fcn: "query",
Args: queryArgs,
})
if err != nil {
fmt.Println("Failed to query: ", err)
}
ret := string(response.Payload)
fmt.Println("Chaincode status: ", response.ChaincodeStatus)
fmt.Println("Payload: ", ret)
return ret
}
go mod init testsdk
go mod tidy
go mod vendor
go run main.go
五、使用Fabric-ca增加peer
1.安装fabric-ca
mkdir -p $GOPATH/src/github.com/hyperledger
cd $GOPATH/src/github.com/hyperledger
1.下载源码
git clone https://github.com/hyperledger/fabric-ca.git
2.编译
go env -w GO111MODULE=
make fabric-ca-server
make fabric-ca-client
3.安装编译好的文件
cp $GOPATH/src/github.com/hyperledger/fabric-ca/bin/* /usr/local/bin
sudo chmod -R 775 /usr/local/bin/fabric-ca-server
sudo chmod -R 775 /usr/local/bin/fabric-ca-client
4.检查
fabric-ca-server version
fabric-ca-server version
2.启动Fabric CA Server并获取CA证书
创建peer1.org1.example.com的msp
1.初始化
mkdir ~/work/example/ca_server
cd ~/work/example/ca_server
fabric-ca-server init -b admin:adminpw --port 7054
2.修改配置文件
ca:
# Name of this CA
name: ca-org1
# Key file (is only used to import a private key into BCCSP)
keyfile: ../organizations/peerOrganizations/org1.example.com/ca/priv_sk
# Certificate file (default: ca-cert.pem)
certfile: ../organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
# Chain file
chainfile:
因9443端口在peer中占用,所以暂时将本配置文件中operations部分注释掉
3.开启server
fabric-ca-server start -b admin:adminpw --port 7054
4.使用client注册账号
mkdir ~/work/example/ca_client
cd ~/work/example/ca_client
export FABRIC_CA_CLIENT_HOME=$PWD
fabric-ca-client enroll -u http://admin:adminpw@localhost:7054
fabric-ca-client register -d --id.name peer1.org1.example.com --id.secret peer1PW --id.type peer -u http://0.0.0.0:7054
5.登陆peer1.org1.example.com账号来获取peer的msp
mkdir ~/work/example/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com
cd ~/work/example/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com
export FABRIC_CA_CLIENT_HOME=$PWD
fabric-ca-client enroll -u http://peer1.org1.example.com:peer1PW@0.0.0.0:7054 -M $FABRIC_CA_CLIENT_HOME/msp
6.声明管理员用户
mkdir msp/admincerts
cp ~/work/example/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem msp/admincerts/
创建peer1.org1.example.com的tls
1.启动TLS server
mkdir ~/work/example/tlsca_server
cd ~/work/example/tlsca_server
fabric-ca-server init -b tlsadmin:tlsadminpw
2.修改配置文件
ca:
# Name of this CA
name: tlsca-org1
# Key file (is only used to import a private key into BCCSP)
keyfile: ../organizations/peerOrganizations/org1.example.com/tlsca/priv_sk
# Certificate file (default: ca-cert.pem)
certfile: ../organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
# Chain file
chainfile:
因9443端口在peer中占用,所以暂时将本配置文件中operations部分注释掉
3.开启server
fabric-ca-server start -b tlsadmin:tlsadminpw --port 7055
4.使用client注册账号
mkdir ~/work/example/tlsca_client
cd ~/work/example/tlsca_client
export FABRIC_CA_CLIENT_HOME=$PWD
fabric-ca-client enroll -u http://tlsadmin:tlsadminpw@localhost:7055
fabric-ca-client register -d --id.name peer1.org1.example.com --id.secret peer1PW --id.type peer -u http://0.0.0.0:7055
5.登陆peer1.org1.example.com账号来获取peer的msp
mkdir ~/work/example/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls
cd ~/work/example/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com
export FABRIC_CA_CLIENT_HOME=$PWD
#注意下方--csr.hosts必须加,否则在将peer加入通道时会报错
fabric-ca-client enroll -u http://peer1.org1.example.com:peer1PW@0.0.0.0:7055 -M $FABRIC_CA_CLIENT_HOME/tls --csr.hosts peer1.org1.example.com
mv tls/keystore/* tls/keystore/server.key
3.启动新增的peer1.org1.example.com
假设peer1的主机ip为138,则全网主机/etc/hosts做如下配置
192.168.1.108 orderer.example.com
192.168.1.112 peer0.org1.example.com
192.168.1.138 peer1.org1.example.com
192.168.1.111 peer0.org2.example.com
然后创建工作目录及获取需要的素材
mkdir -p work/example/peer
cd work/example
scp -r dev2@192.168.1.112:~/work/example/organizations organizations
创建core.yaml,对比peer0的core.yaml做如下修改
cd peer
15 peer.id: peer1.org1.example.com
162 peer.gossip.externalEndpoint: peer1.org1.example.com:7051
254 peer.tls.cert.file: ../organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/signcerts/cert.pem
258 peer.tls.key.file: ../organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/keystore/server.key
261 peer.tls.rootcert.file:
../organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/cacerts/0-0-0-0-7055.pem
265 peer.tls.clientRootCAs.files:
- ../organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/cacerts/0-0-0-0-7055.pem
314 peer.mspConfigPath: ../organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp
323 peer.localMspId: Org1MSP
启动peer
export FABRIC_CFG_PATH=$PWD
peer node start >> log_peer.log 2>&1 &
peer在加入通道前,log会出现错误提示,暂时先忽略,如bad certificate server
4.将peer加入通道
系统已经有了通道channel1,peer1从Orderer获取通道的创始区块,命令中0来指定要获取的是创始区块。
#export FABRIC_CFG_PATH=$PWD 不设置此变量
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
自定义orderer的tls ca证书变量
export ORDERER_TLSCA=${PWD}/../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
mkdir channel-artifacts
peer channel fetch 0 ./channel-artifacts/channel_org1.block -o orderer.example.com:7050 -c channel1 --tls --cafile $ORDERER_TLSCA
peer channel join -b ./channel-artifacts/channel_org1.block
此处如报错Error: error getting endorser client for channel: endorser client failed to connect to peer1.org1.example.com:7051: failed to create new connection: context deadline exceeded
是因为怀疑是TLS CA在登陆时,没有指定--csr.hosts参数
5.安装并调用链码
首先将链代码复制到peer1的主机上
cd ~/work/example/peer
mkdir chaincode
scp -r dev2@192.168.1.112:~/work/example/peer/chaincode/sacc chaincode/sacc
安装依赖包
rm -rf chaincode/sacc/vendor/
cd chaincode/sacc
go env -w GO111MODULE=on
go mod init sacc
go mod tidy
go mod vendor
安装
cd ~/work/example/peer
# export FABRIC_CFG_PATH=$PWD 此环境变量不设置
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
peer lifecycle chaincode package chaincode/sacc.tar.gz --path chaincode/sacc --lang golang --label sacc_1
peer lifecycle chaincode install chaincode/sacc.tar.gz
peer lifecycle chaincode queryinstalled #查询确认一下
安装完毕,不必再审议链码,可以直接调用(只有新增组织时需要审议)
如果报错,等待几分钟,即可
调用链码
自定义orderer的tls ca证书变量
export ORDERER_TLSCA=${PWD}/../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer chaincode query -C channel1 -n sacc -c '{"Args":["query","a"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc -c '{"Args":["get","a"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc --peerAddresses peer1.org1.example.com:7051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles ${PWD}/../organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["set","a","cc"]}'
调用链码,背书节点必须满足策略,当前默认为大多数组织同意,即必须有属于两个组织的peer才能更新数据成功,否则随提交成功,但并没有更新数据
六、增加组织(未设置锚节点)
在192.168.1.138的主机上增加组织Org3的一个peer
1.获取身份证书
因此处直接创建组织,所以直接使用cryptogen工具来创建即可,不必使用Fabric CA
mkdir -p ~/work/example_org3/
cd ~/work/example_org3/
编辑配置文件vim org3-crypto.yaml
PeerOrgs:
# ---------------------------------------------------------------------------
# Org3
# ---------------------------------------------------------------------------
- Name: Org3MSP
Domain: org3.example.com
EnableNodeOUs: true
Template:
Count: 1
Users:
Count: 1
生成文件
cryptogen generate --config=org3-crypto.yaml --output="./organizations"
2.启动peer0.org3.example.com
mkdir ~/work/example_org3/peer
cd ~/work/example_org3/peer
vim core.yaml
对照org1的core.yaml做以下修改
15 peer.id: peer0.org3.example.com
23 peer.listenAddress: 0.0.0.0:9051
28 peer.chaincodeListenAddress: 0.0.0.0:9052
40 peer.address: 0.0.0.0:9051
86 peer.gossip.bootstrap: 127.0.0.1:9051
162 peer.gossip.externalEndpoint: peer0.org3.example.com:9051
254 peer.tls.cert.file: ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/server.crt
258 peer.tls.key.file: ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/server.key
261 peer.tls.rootcert.file:
../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
265 peer.tls.clientRootCAs.files:
- ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
314 peer.mspConfigPath: ../organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp
323 peer.localMspId: Org3MSP
681 operations.listenAddress: 127.0.0.1:9445 #因本机上还有个peer使用了9443
修改各主机/etc/hosts
192.168.1.108 orderer.example.com
192.168.1.112 peer0.org1.example.com
192.168.1.138 peer1.org1.example.com
192.168.1.111 peer0.org2.example.com
192.168.1.138 peer0.org3.example.com
启动
export FABRIC_CFG_PATH=$PWD
peer node start
3.获取配置区块
Org3还不是通道成员,我们需要以其它组织的admin来拉取通道配置。Org1组织。因Org1的peer1.org1.example.com也在Org3的主机上,因此直接用本地路径。生产环境要传输获取的区块。
cd ~/work/example_org3/peer
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../../example/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../../example/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
#自定义变量
export ORDERER_TLSCA=${PWD}/../../example/organizations/orderer.example.com/tlscacerts/tlsca.example.com-cert.pem
#拉取配置区块
mkdir channel-artifacts
peer channel fetch config ./channel-artifacts/config_block.pb -o orderer.example.com:7050 -c channel1 --tls --cafile $ORDERER_TLSCA
4.创建Org3的configtx.yaml
打开新的终端,屏蔽之前的环境变量影响
cd ~/work/example_org3
创建configtx.yaml
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
---
################################################################################
#
# Section: Organizations
#
# - This section defines the different organizational identities which will
# be referenced later in the configuration.
#
################################################################################
Organizations:
- &Org3
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: Org3MSP
# ID to load the MSP definition as
ID: Org3MSP
MSPDir: ./organizations/peerOrganizations/org3.example.com/msp
Policies:
Readers:
Type: Signature
Rule: "OR('Org3MSP.admin', 'Org3MSP.peer', 'Org3MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org3MSP.admin', 'Org3MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org3MSP.admin')"
Endorsement:
Type: Signature
Rule: "OR('Org3MSP.peer')"
AnchorPeers:
# AnchorPeers defines the location of peers which can be used
# for cross org gossip communication. Note, this value is only
# encoded in the genesis block in the Application section context
- Host: peer0.org3.example.com
Port: 9051
执行如下命令创建org3.json
export FABRIC_CFG_PATH=$PWD
configtxgen -printOrg Org3MSP > ./organizations/peerOrganizations/org3.example.com/org3.json
5.修改配置
cd channel-artifacts
export CHANNEL_NAME=channel1
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ../../organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
configtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output org3_update.pb
configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL_NAME'", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json
configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb
6.提交修改
在通道写入配置之前,当前默认策略是需要大多数组织同意,所以需要大多数组织管理员签名。
先以Org1的组织管理员来签名
cd ~/work/example_org3/peer/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../../example/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../../example/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
#自定义变量
export ORDERER_TLSCA=${PWD}/../../example/organizations/orderer.example.com/tlscacerts/tlsca.example.com-cert.pem
peer channel signconfigtx -f ./channel-artifacts/org3_update_in_envelope.pb
然后Org2的管理员来签名,首先将org3_update_in_envelope.pb传输给Org2的某个peer主机
在peer0.org2.example.com上
cd ~/work/example/peer
scp dev4@192.168.1.138:~/work/example_org3/peer/channel-artifacts/org3_update_in_envelope.pb ./channel-artifacts/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export ORDERER_TLSCA=${PWD}/../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer channel update -f ./channel-artifacts/org3_update_in_envelope.pb -c channel1 -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA
此时配置区块会升级到block 3
7.将Org3的peer加入通道
回到192.168.1.38的主机上(Org3的peer所在主机)
cd ~/work/example_org3/peer
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org3MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=peer0.org3.example.com:9051
export ORDERER_TLSCA=${PWD}/../../example/organizations/orderer.example.com/tlscacerts/tlsca.example.com-cert.pem
获取区块
peer channel fetch 0 ./channel-artifacts/channel1.block -o orderer.example.com:7050 -c channel1 --tls --cafile $ORDERER_TLSCA
注意,我们传递了0来标识想要通道上的第一个区块:创始区块。如果我们直接使用peer channel fetch config,我们会接收block 3(升级Org3定义的区块)。然而我们不能从后续的区块开始,我们必须从区块0开始。
peer channel join -b ./channel-artifacts/channel1.block
8.安装调用链码
打开新的终端,复制链码
cp -r ../../example/peer/chaincode/ .
rm chaincode/sacc.tar.gz
安装链码(与新增peer不同,新增组织必须要审议链码)
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org3MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=peer0.org3.example.com:9051
export ORDERER_TLSCA=${PWD}/../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer lifecycle chaincode package chaincode/sacc.tar.gz --path chaincode/sacc --lang golang --label sacc_1
peer lifecycle chaincode install chaincode/sacc.tar.gz
peer lifecycle chaincode queryinstalled
export CC_PACKAGE_ID=sacc_1:b33357c4012471d8bd96ba48fd2a12ada5fedfbfd6d623590295778500a0368d
#注意与增加节点不同,新增组织必须要审议以下链码,才能调用链码
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --channelID channel1 --init-required --name sacc --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_TLSCA
#审议结果可以通过peer lifecycle chaincode querycommitted --channelID channel1 --name sacc --cafile $ORDERER_TLSCA查询
调用链码
peer chaincode query -C channel1 -n sacc -c '{"Args":["query","a"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc --peerAddresses peer0.org3.example.com:9051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles ${PWD}/../../example/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["set","a","2255"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc --peerAddresses peer0.org3.example.com:9051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.org1.example.com:7051 --tlsRootCertFiles ${PWD}/../../example/organizations/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/signcerts/cert.pem -c '{"Args":["set","a","3355"]}'
七、链码中用户权限
链码中用户权限有三种方式:参见https://github.com/hyperledger/fabric-chaincode-go/blob/master/pkg/cid/README.md
- 用户的属性
- 用户的id及MspId
- 用户身份的OU(组织单元organizational unit)
1.使用用户属性来控制权限
属性的两种使用方式
方式一,获取属性值
val, ok, err := cid.GetAttributeValue(stub, "attr1")
if err != nil {
// 获取属性时有错误
}
if !ok {
// 没有此属性
}
// 变量val为属性的值
方式二,判断属性值是否为某个值
err := cid.AssertAttributeValue(stub, "attr2", "true")
if err != nil {
// 如果attr2的值不是"true",则会有err
} else {
//是"true",执行这里
}
编辑链码fabric-samples中的示例代码sacc.go。
假设需求是,拥有属性addPrefix的用户设置值时加个前缀prefix_。没有此属性的用户设置时不加前缀。更改Invoke函数。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
err := cid.AssertAttributeValue(stub, "addPrefix", "true")
if err == nil {
args = [] string {args[0], "prefix_"+args[1]}
}
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
在Org1的peer0和Org2的peer0上升级链码,Org3暂时不升级(不升级链码的组织用户调用链码时会报错,无法调用)。(因各种问题,作者的链码版本此时已升级到第4版,按第4版贴出部分安装调用代码)
在Org1和Org2的peer上执行以下代码,环境变量按前文设置,此时不再贴出。
peer lifecycle chaincode package chaincode/sacc4.tar.gz --path chaincode/sacc4 --lang golang --label sacc_4
peer lifecycle chaincode install chaincode/sacc4.tar.gz
export CC_PACKAGE_ID=sacc_4:46d5aed45040c0d190081a8cb3fd550b6e6af4cafadc742e707d2af44bd35650
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --channelID channel1 --init-required --name sacc --version 4.0 --package-id $CC_PACKAGE_ID --sequence 4 --tls --cafile $ORDERER_TLSCA
peer lifecycle chaincode checkcommitreadiness --channelID channel1 --init-required --name sacc --version 4.0 --sequence 4 --tls --cafile $ORDERER_TLSCA --output json
提交并初始化链码
peer lifecycle chaincode commit -o orderer.example.com:7050 --channelID channel1 --init-required --name sacc --version 4.0 --sequence 4 --tls --cafile $ORDERER_TLSCA --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles ${PWD}/../organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles ${PWD}/../organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"Args":["a","bb"]}'
创建Org1的用户user2
首先,启动peer0.org1.example.com上之前的Fabric CA server
cd ~/work/example/ca_server
fabric-ca-server start -b admin:adminpw --port 7054
注册包含属性addPrefix的用户
cd ~/work/example/ca_client
export FABRIC_CA_CLIENT_HOME=$PWD
fabric-ca-client register --id.name user2 --id.secret user2pw --id.type user --id.attrs 'addPrefix=true:ecert'
获取msp
cd ~/work/example/organizations/peerOrganizations/org1.example.com/users/
mkdir User2@org1.example.com
cd User2@org1.example.com
export FABRIC_CA_CLIENT_HOME=$PWD
fabric-ca-client enroll -u http://user2:user2pw@0.0.0.0:7054 -M $FABRIC_CA_CLIENT_HOME/msp
#生成管理员证书
cd msp
mkdir admincerts
cp signcerts/cert.pem admincerts/
使用user2调用链码
cd ~/work/example/peer/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
#此处不单独获取tls证书,直接使用peer的tls证书,效果一样。生产环境中需要单独获取tls证书,流程参见5.2部分
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/../organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/../organizations/peerOrganizations/org1.example.com/users/User2@org1.example.com/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export ORDERER_TLSCA=${PWD}/../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
#查询当前值
peer chaincode query -C channel1 -n sacc -c '{"Args":["query","a"]}'
#设置值为22,结果应为prefix_22
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_TLSCA -C channel1 -n sacc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles ${PWD}/../organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["set","a","22"]}'
此时切换其它账号设置,即可看到只有user2设置值时会加前缀prefix_
2.用户(客户端)的id及MSP ID
用户ID的获取方式
import "github.com/hyperledger/fabric-chaincode-go/pkg/cid"
//方式一:
id, err := cid.GetID(stub) //此id为字符串,看源代码知其实是调用方式二
//方式二:
c, err := cid.New(stub)
id, err := c.GetID()
//拓展阅读id的源代码如下
//_id := fmt.Sprintf("x509::%s::%s", getDN(&c.cert.Subject), getDN(&c.cert.Issuer))
//id = base64.StdEncoding.EncodeToString([]byte(_id))
获取 MSP ID(即组织ID)
import "github.com/hyperledger/fabric-chaincode-go/pkg/cid"
//方式一
mspid, err := cid.GetMSPID(stub) //看源代码知,其实是调用方式二
//方式二
c, err := cid.New(stub) //c为ClientID结构体的实例化
mspid, err := c.GetMSPID() //实为c.mspID
//拓展阅读结构体ClientID
type ClientID struct {
stub ChaincodeStubInterface
mspID string
cert *x509.Certificate
attrs *attrmgr.Attributes
}
升级链码,加上功能,打印用户的id及MSPID,修改Invoke部分
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
err := cid.AssertAttributeValue(stub, "addPrefix", "true")
if err == nil {
args = [] string {args[0], "prefix_"+args[1]}
}
c, err := cid.New(stub)
id, err := c.GetID()
mspid, err := c.GetMSPID()
fmt.Println("*************************************************************************************\n")
fmt.Printf("id=%s, mspid=%s", id, mspid)
fmt.Println("*************************************************************************************\n")
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
在Org1和Org2的peer上安装,审议,提交链码。注意此处为链码升级,因此在审议链码时,参数--name值不变(此处为sacc),如果--name 变化就是安装其它链码与已安装的链码无关。
启动后调用链码,查看链码日志输出
1. docker ps 找到对应链码版本的容器ID
2. docker logs -f 容器ID
输出内容为(修饰过展示,便于理解)
id = eDUwOTo6Q049QWRtaW5Ab3JnMS5leGFtcGxlLmNvbSxMPVNhbiBGcmFuY2lzY28sU1Q9Q2FsaWZvcm5pYSxDPVVTOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPVNhbiBGcmFuY2lzY28sU1Q9Q2FsaWZvcm5pYSxDPVVT
mspid = Org1MSP
3.从属关系OU
请注意,ECert的“OU”或组织单位总是根据identities type和affiliance的值设置的。对于注册,OU计算为OU=<type>,OU=<affiliationRoot>,…,OU=<affiliationLeaf>。例如,一个隶属关系为“org1.dept2.team3”的“client”类型的标识将具有以下组织单位:OU=client,OU=org1,OU=dept2,OU=team3
found, err := cid.HasOUValue(stub, "org1") //也可以c, err := cid.New(stub) found, err := c.HasOUValue("org1")
if err != nil {
// Return an error
}
if !found {
// The client identity is not part of the Organizational Unit
// Return an error
}
//拓展阅读,贴出源码
func (c *ClientID) HasOUValue(OUValue string) (bool, error) {
x509Cert := c.cert
if x509Cert == nil {
// Here it will return false and an error, as there is no x509 type cert to check for OU values.
return false, fmt.Errorf("cannot obtain an X509 certificate for the identity")
}
for _, OU := range x509Cert.Subject.OrganizationalUnit {
if OU == OUValue {
return true, nil
}
}
return false, nil
}
//因此使用时比如某个用户注册时是
fabric-ca-client register --id.name user2o --id.secret userpw --id.type client --id.affiliation org2.department1
//假如链码如下所示:
x509Cert, err := c.GetX509Certificate()
if err != nil {
fmt.Println("no cert!!!")
} else {
for _, OU := range x509Cert.Subject.OrganizationalUnit {
fmt.Printf("OU=%s\n", OU)
}
}
found, err := c.HasOUValue("org2")
if found {
fmt.Println("has org2")
} else {
fmt.Println("no org2!!!")
}
found, err = c.HasOUValue("department1")
if found {
fmt.Println("has department1")
} else {
fmt.Println("no department1 !!!")
}
found, err = c.HasOUValue("org2.department1")
if found {
fmt.Println("has org2.department1")
} else {
fmt.Println("no org2.department1 !!!")
}
//链码截止
//那么输出为
// OU=client
// OU=org2
// OU=department1
// has org2
// has department1
// no org2.department1 !!!
// 因此HasOUValue函数不能判断org2.department1,只能判断org2和department1。
查看user的OU,修改invoke部分
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
err := cid.AssertAttributeValue(stub, "addPrefix", "true")
if err == nil {
args = [] string {args[0], "prefix_"+args[1]}
}
c, err := cid.New(stub)
id, err := c.GetID()
mspid, err := c.GetMSPID()
fmt.Println("*************************************************************************************\n")
fmt.Printf("id=%s\n", id)
fmt.Printf("mspid=%s\n", mspid)
//输出所属的OU
x509Cert, err := c.GetX509Certificate()
if err != nil {
fmt.Println("no cert!!!")
} else {
for _, OU := range x509Cert.Subject.OrganizationalUnit {
fmt.Printf("OU=%s\n", OU)
}
}
fmt.Println("*************************************************************************************\n")
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
输出结果
当用户为Admin或User1:(cryptogen工具生成的用户没有OU)
OU部分输出为空,开始时crypto-config.yaml 中没有设置EnableNodeOUs: true,设置后猜测应该此处有输出。待验证
当用户为User2 (Fabric CA注册的用户时只有--id.type user)
OU=user
注册新账号
fabric-ca-client register --id.name user2o --id.secret userpw --id.type client --id.affiliation org2.department1
注意:
1.此处的affiliation中org2并不是联盟链中Org2MSP,仍然数以Org1MSP。此字段可以理解为一个属性,本身并不代表在原生链中的权限。注意在正式开发中要区分org1和org2
2.--id.affiliation的值只能从Fabric CA Server配置文件中选择(可以修改配置文件),当前文件内容为:
affiliations:
org1:
- department1
- department2
org2:
- department1
--id.affiliation值可以是:
org1
org1.department1
以user2o调用连码,查看输出结果
OU=client
OU=org2
OU=department1
4.混合使用
id, err := cid.New(stub)
if err != nil {
// Handle error
}
mspid, err := id.GetMSPID()
if err != nil {
// Handle error
}
switch mspid {
case "org1MSP":
err = id.AssertAttributeValue("attr1", "true")
case "org2MSP":
err = id.AssertAttributeValue("attr2", "true")
default:
err = errors.New("Wrong MSP")
}