Hyperledger Fabric 2.2实战记录(二)

四、使用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")
}

 

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值