From f1c390e798e971f155f10e928ca361a0b4d0fb0a Mon Sep 17 00:00:00 2001 From: Sandra Vrtikapa Date: Tue, 12 Sep 2017 14:13:49 -0400 Subject: [PATCH] [FAB-6314] Channel Client Change-Id: Iaa9bd6dd64c854124c040205f0f3c37cdcd308e7 Signed-off-by: Sandra Vrtikapa --- api/apiconfig/configprovider.go | 2 + api/apiconfig/mocks/mockconfig.gen.go | 256 ++++++++++-------- api/apiconfig/network.go | 6 + api/apifabca/mocks/mockfabriccaclient.gen.go | 46 ++-- api/apifabclient/credentialmgr.go | 22 ++ api/apifabclient/discoveryprovider.go | 17 ++ api/apifabclient/fabricclient.go | 3 +- api/apifabclient/signingmgr.go | 14 + api/apitxn/mocks/mockapitxn.gen.go | 14 +- api/apitxn/txn.go | 105 +++++++ def/fabapi/context/context.go | 2 + def/fabapi/context/defprovider/org.go | 14 + def/fabapi/context/defprovider/sdk.go | 14 + def/fabapi/context/defprovider/session.go | 107 ++++++++ def/fabapi/context/provider.go | 6 +- def/fabapi/fabapi.go | 130 ++++++++- def/fabapi/fabapi_test.go | 8 +- def/fabapi/pkgfactory.go | 71 ++--- pkg/config/config.go | 107 ++++++-- pkg/config/config_test.go | 21 +- pkg/fabric-ca-client/mocks/mockconfig.go | 10 + pkg/fabric-client/channel/channel.go | 5 +- pkg/fabric-client/channel/txnproposer.go | 17 +- pkg/fabric-client/channel/txnsender.go | 11 +- pkg/fabric-client/client.go | 47 +++- pkg/fabric-client/client_test.go | 14 + .../credentialmgr/credentialmgr.go | 125 +++++++++ .../credentialmgr/credentialmgr_test.go | 69 +++++ pkg/fabric-client/events/consumer/consumer.go | 18 +- pkg/fabric-client/internal/util.go | 16 -- pkg/fabric-client/mocks/mockclient.go | 29 +- pkg/fabric-client/mocks/mockconfig.go | 10 + pkg/fabric-client/mocks/mocksigningmgr.go | 30 ++ pkg/fabric-client/signingmgr/signingmgr.go | 52 ++++ .../signingmgr/signingmgr_test.go | 49 ++++ pkg/fabric-txn/chclient/chclient.go | 233 ++++++++++++++++ pkg/fabric-txn/chclient/chclient_test.go | 213 +++++++++++++++ pkg/fabric-txn/discovery/staticdiscovery.go | 68 +++++ .../discovery/staticdiscovery_test.go | 75 +++++ pkg/fabric-txn/mocks/mockdiscovery.go | 56 ++++ test/fixtures/config/config_pkcs11_test.yaml | 20 +- test/fixtures/config/config_test.yaml | 31 ++- test/integration/base_test_setup.go | 61 +---- test/integration/channel_client_test.go | 242 +++++++++++++++++ test/integration/channel_queries_test.go | 5 - test/integration/fabric_ca_test.go | 4 +- test/integration/install_chaincode_test.go | 3 - test/integration/main_test.go | 2 +- test/integration/orgs/test_setup.go | 68 +++-- test/integration/utils.go | 83 +----- 50 files changed, 2159 insertions(+), 472 deletions(-) create mode 100644 api/apifabclient/credentialmgr.go create mode 100644 api/apifabclient/discoveryprovider.go create mode 100644 api/apifabclient/signingmgr.go create mode 100644 api/apitxn/txn.go create mode 100644 pkg/fabric-client/credentialmgr/credentialmgr.go create mode 100644 pkg/fabric-client/credentialmgr/credentialmgr_test.go create mode 100644 pkg/fabric-client/mocks/mocksigningmgr.go create mode 100644 pkg/fabric-client/signingmgr/signingmgr.go create mode 100644 pkg/fabric-client/signingmgr/signingmgr_test.go create mode 100644 pkg/fabric-txn/chclient/chclient.go create mode 100644 pkg/fabric-txn/chclient/chclient_test.go create mode 100644 pkg/fabric-txn/discovery/staticdiscovery.go create mode 100644 pkg/fabric-txn/discovery/staticdiscovery_test.go create mode 100644 pkg/fabric-txn/mocks/mockdiscovery.go create mode 100644 test/integration/channel_client_test.go diff --git a/api/apiconfig/configprovider.go b/api/apiconfig/configprovider.go index dfe7522e9e..a5047fd749 100644 --- a/api/apiconfig/configprovider.go +++ b/api/apiconfig/configprovider.go @@ -28,6 +28,8 @@ type Config interface { PeersConfig(org string) ([]PeerConfig, error) PeerConfig(org string, name string) (*PeerConfig, error) NetworkConfig() (*NetworkConfig, error) + ChannelConfig(name string) (*ChannelConfig, error) + ChannelPeers(name string) ([]ChannelPeer, error) IsTLSEnabled() bool SetTLSCACertPool(*x509.CertPool) TLSCACertPool(tlsCertificate string) (*x509.CertPool, error) diff --git a/api/apiconfig/mocks/mockconfig.gen.go b/api/apiconfig/mocks/mockconfig.gen.go index fd8a1cbcb5..950306aaac 100644 --- a/api/apiconfig/mocks/mockconfig.gen.go +++ b/api/apiconfig/mocks/mockconfig.gen.go @@ -1,6 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/hyperledger/fabric-sdk-go/api/apiconfig (interfaces: Config) +// Package mock_apiconfig is a generated GoMock package. package mock_apiconfig import ( @@ -8,6 +9,7 @@ import ( gomock "github.com/golang/mock/gomock" apiconfig "github.com/hyperledger/fabric-sdk-go/api/apiconfig" factory "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" + reflect "reflect" time "time" ) @@ -30,353 +32,379 @@ func NewMockConfig(ctrl *gomock.Controller) *MockConfig { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockConfig) EXPECT() *MockConfigMockRecorder { - return _m.recorder +func (m *MockConfig) EXPECT() *MockConfigMockRecorder { + return m.recorder } // CAClientCertFile mocks base method -func (_m *MockConfig) CAClientCertFile(_param0 string) (string, error) { - ret := _m.ctrl.Call(_m, "CAClientCertFile", _param0) +func (m *MockConfig) CAClientCertFile(arg0 string) (string, error) { + ret := m.ctrl.Call(m, "CAClientCertFile", arg0) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // CAClientCertFile indicates an expected call of CAClientCertFile -func (_mr *MockConfigMockRecorder) CAClientCertFile(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAClientCertFile", arg0) +func (mr *MockConfigMockRecorder) CAClientCertFile(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAClientCertFile", reflect.TypeOf((*MockConfig)(nil).CAClientCertFile), arg0) } // CAClientKeyFile mocks base method -func (_m *MockConfig) CAClientKeyFile(_param0 string) (string, error) { - ret := _m.ctrl.Call(_m, "CAClientKeyFile", _param0) +func (m *MockConfig) CAClientKeyFile(arg0 string) (string, error) { + ret := m.ctrl.Call(m, "CAClientKeyFile", arg0) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // CAClientKeyFile indicates an expected call of CAClientKeyFile -func (_mr *MockConfigMockRecorder) CAClientKeyFile(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAClientKeyFile", arg0) +func (mr *MockConfigMockRecorder) CAClientKeyFile(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAClientKeyFile", reflect.TypeOf((*MockConfig)(nil).CAClientKeyFile), arg0) } // CAConfig mocks base method -func (_m *MockConfig) CAConfig(_param0 string) (*apiconfig.CAConfig, error) { - ret := _m.ctrl.Call(_m, "CAConfig", _param0) +func (m *MockConfig) CAConfig(arg0 string) (*apiconfig.CAConfig, error) { + ret := m.ctrl.Call(m, "CAConfig", arg0) ret0, _ := ret[0].(*apiconfig.CAConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // CAConfig indicates an expected call of CAConfig -func (_mr *MockConfigMockRecorder) CAConfig(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAConfig", arg0) +func (mr *MockConfigMockRecorder) CAConfig(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAConfig", reflect.TypeOf((*MockConfig)(nil).CAConfig), arg0) } // CAKeyStorePath mocks base method -func (_m *MockConfig) CAKeyStorePath() string { - ret := _m.ctrl.Call(_m, "CAKeyStorePath") +func (m *MockConfig) CAKeyStorePath() string { + ret := m.ctrl.Call(m, "CAKeyStorePath") ret0, _ := ret[0].(string) return ret0 } // CAKeyStorePath indicates an expected call of CAKeyStorePath -func (_mr *MockConfigMockRecorder) CAKeyStorePath() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAKeyStorePath") +func (mr *MockConfigMockRecorder) CAKeyStorePath() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAKeyStorePath", reflect.TypeOf((*MockConfig)(nil).CAKeyStorePath)) } // CAServerCertFiles mocks base method -func (_m *MockConfig) CAServerCertFiles(_param0 string) ([]string, error) { - ret := _m.ctrl.Call(_m, "CAServerCertFiles", _param0) +func (m *MockConfig) CAServerCertFiles(arg0 string) ([]string, error) { + ret := m.ctrl.Call(m, "CAServerCertFiles", arg0) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // CAServerCertFiles indicates an expected call of CAServerCertFiles -func (_mr *MockConfigMockRecorder) CAServerCertFiles(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAServerCertFiles", arg0) +func (mr *MockConfigMockRecorder) CAServerCertFiles(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAServerCertFiles", reflect.TypeOf((*MockConfig)(nil).CAServerCertFiles), arg0) } // CSPConfig mocks base method -func (_m *MockConfig) CSPConfig() *factory.FactoryOpts { - ret := _m.ctrl.Call(_m, "CSPConfig") +func (m *MockConfig) CSPConfig() *factory.FactoryOpts { + ret := m.ctrl.Call(m, "CSPConfig") ret0, _ := ret[0].(*factory.FactoryOpts) return ret0 } // CSPConfig indicates an expected call of CSPConfig -func (_mr *MockConfigMockRecorder) CSPConfig() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CSPConfig") +func (mr *MockConfigMockRecorder) CSPConfig() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CSPConfig", reflect.TypeOf((*MockConfig)(nil).CSPConfig)) +} + +// ChannelConfig mocks base method +func (m *MockConfig) ChannelConfig(arg0 string) (*apiconfig.ChannelConfig, error) { + ret := m.ctrl.Call(m, "ChannelConfig", arg0) + ret0, _ := ret[0].(*apiconfig.ChannelConfig) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChannelConfig indicates an expected call of ChannelConfig +func (mr *MockConfigMockRecorder) ChannelConfig(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChannelConfig", reflect.TypeOf((*MockConfig)(nil).ChannelConfig), arg0) +} + +// ChannelPeers mocks base method +func (m *MockConfig) ChannelPeers(arg0 string) ([]apiconfig.ChannelPeer, error) { + ret := m.ctrl.Call(m, "ChannelPeers", arg0) + ret0, _ := ret[0].([]apiconfig.ChannelPeer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChannelPeers indicates an expected call of ChannelPeers +func (mr *MockConfigMockRecorder) ChannelPeers(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChannelPeers", reflect.TypeOf((*MockConfig)(nil).ChannelPeers), arg0) } // Client mocks base method -func (_m *MockConfig) Client() (*apiconfig.ClientConfig, error) { - ret := _m.ctrl.Call(_m, "Client") +func (m *MockConfig) Client() (*apiconfig.ClientConfig, error) { + ret := m.ctrl.Call(m, "Client") ret0, _ := ret[0].(*apiconfig.ClientConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // Client indicates an expected call of Client -func (_mr *MockConfigMockRecorder) Client() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Client") +func (mr *MockConfigMockRecorder) Client() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Client", reflect.TypeOf((*MockConfig)(nil).Client)) } // CryptoConfigPath mocks base method -func (_m *MockConfig) CryptoConfigPath() string { - ret := _m.ctrl.Call(_m, "CryptoConfigPath") +func (m *MockConfig) CryptoConfigPath() string { + ret := m.ctrl.Call(m, "CryptoConfigPath") ret0, _ := ret[0].(string) return ret0 } // CryptoConfigPath indicates an expected call of CryptoConfigPath -func (_mr *MockConfigMockRecorder) CryptoConfigPath() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CryptoConfigPath") +func (mr *MockConfigMockRecorder) CryptoConfigPath() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CryptoConfigPath", reflect.TypeOf((*MockConfig)(nil).CryptoConfigPath)) } // Ephemeral mocks base method -func (_m *MockConfig) Ephemeral() bool { - ret := _m.ctrl.Call(_m, "Ephemeral") +func (m *MockConfig) Ephemeral() bool { + ret := m.ctrl.Call(m, "Ephemeral") ret0, _ := ret[0].(bool) return ret0 } // Ephemeral indicates an expected call of Ephemeral -func (_mr *MockConfigMockRecorder) Ephemeral() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Ephemeral") +func (mr *MockConfigMockRecorder) Ephemeral() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ephemeral", reflect.TypeOf((*MockConfig)(nil).Ephemeral)) } // IsSecurityEnabled mocks base method -func (_m *MockConfig) IsSecurityEnabled() bool { - ret := _m.ctrl.Call(_m, "IsSecurityEnabled") +func (m *MockConfig) IsSecurityEnabled() bool { + ret := m.ctrl.Call(m, "IsSecurityEnabled") ret0, _ := ret[0].(bool) return ret0 } // IsSecurityEnabled indicates an expected call of IsSecurityEnabled -func (_mr *MockConfigMockRecorder) IsSecurityEnabled() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "IsSecurityEnabled") +func (mr *MockConfigMockRecorder) IsSecurityEnabled() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSecurityEnabled", reflect.TypeOf((*MockConfig)(nil).IsSecurityEnabled)) } // IsTLSEnabled mocks base method -func (_m *MockConfig) IsTLSEnabled() bool { - ret := _m.ctrl.Call(_m, "IsTLSEnabled") +func (m *MockConfig) IsTLSEnabled() bool { + ret := m.ctrl.Call(m, "IsTLSEnabled") ret0, _ := ret[0].(bool) return ret0 } // IsTLSEnabled indicates an expected call of IsTLSEnabled -func (_mr *MockConfigMockRecorder) IsTLSEnabled() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "IsTLSEnabled") +func (mr *MockConfigMockRecorder) IsTLSEnabled() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsTLSEnabled", reflect.TypeOf((*MockConfig)(nil).IsTLSEnabled)) } // KeyStorePath mocks base method -func (_m *MockConfig) KeyStorePath() string { - ret := _m.ctrl.Call(_m, "KeyStorePath") +func (m *MockConfig) KeyStorePath() string { + ret := m.ctrl.Call(m, "KeyStorePath") ret0, _ := ret[0].(string) return ret0 } // KeyStorePath indicates an expected call of KeyStorePath -func (_mr *MockConfigMockRecorder) KeyStorePath() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "KeyStorePath") +func (mr *MockConfigMockRecorder) KeyStorePath() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyStorePath", reflect.TypeOf((*MockConfig)(nil).KeyStorePath)) } // MspID mocks base method -func (_m *MockConfig) MspID(_param0 string) (string, error) { - ret := _m.ctrl.Call(_m, "MspID", _param0) +func (m *MockConfig) MspID(arg0 string) (string, error) { + ret := m.ctrl.Call(m, "MspID", arg0) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // MspID indicates an expected call of MspID -func (_mr *MockConfigMockRecorder) MspID(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "MspID", arg0) +func (mr *MockConfigMockRecorder) MspID(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MspID", reflect.TypeOf((*MockConfig)(nil).MspID), arg0) } // NetworkConfig mocks base method -func (_m *MockConfig) NetworkConfig() (*apiconfig.NetworkConfig, error) { - ret := _m.ctrl.Call(_m, "NetworkConfig") +func (m *MockConfig) NetworkConfig() (*apiconfig.NetworkConfig, error) { + ret := m.ctrl.Call(m, "NetworkConfig") ret0, _ := ret[0].(*apiconfig.NetworkConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // NetworkConfig indicates an expected call of NetworkConfig -func (_mr *MockConfigMockRecorder) NetworkConfig() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "NetworkConfig") +func (mr *MockConfigMockRecorder) NetworkConfig() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkConfig", reflect.TypeOf((*MockConfig)(nil).NetworkConfig)) } // OrdererConfig mocks base method -func (_m *MockConfig) OrdererConfig(_param0 string) (*apiconfig.OrdererConfig, error) { - ret := _m.ctrl.Call(_m, "OrdererConfig", _param0) +func (m *MockConfig) OrdererConfig(arg0 string) (*apiconfig.OrdererConfig, error) { + ret := m.ctrl.Call(m, "OrdererConfig", arg0) ret0, _ := ret[0].(*apiconfig.OrdererConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // OrdererConfig indicates an expected call of OrdererConfig -func (_mr *MockConfigMockRecorder) OrdererConfig(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "OrdererConfig", arg0) +func (mr *MockConfigMockRecorder) OrdererConfig(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrdererConfig", reflect.TypeOf((*MockConfig)(nil).OrdererConfig), arg0) } // OrderersConfig mocks base method -func (_m *MockConfig) OrderersConfig() ([]apiconfig.OrdererConfig, error) { - ret := _m.ctrl.Call(_m, "OrderersConfig") +func (m *MockConfig) OrderersConfig() ([]apiconfig.OrdererConfig, error) { + ret := m.ctrl.Call(m, "OrderersConfig") ret0, _ := ret[0].([]apiconfig.OrdererConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // OrderersConfig indicates an expected call of OrderersConfig -func (_mr *MockConfigMockRecorder) OrderersConfig() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "OrderersConfig") +func (mr *MockConfigMockRecorder) OrderersConfig() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrderersConfig", reflect.TypeOf((*MockConfig)(nil).OrderersConfig)) } // PeerConfig mocks base method -func (_m *MockConfig) PeerConfig(_param0 string, _param1 string) (*apiconfig.PeerConfig, error) { - ret := _m.ctrl.Call(_m, "PeerConfig", _param0, _param1) +func (m *MockConfig) PeerConfig(arg0, arg1 string) (*apiconfig.PeerConfig, error) { + ret := m.ctrl.Call(m, "PeerConfig", arg0, arg1) ret0, _ := ret[0].(*apiconfig.PeerConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // PeerConfig indicates an expected call of PeerConfig -func (_mr *MockConfigMockRecorder) PeerConfig(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "PeerConfig", arg0, arg1) +func (mr *MockConfigMockRecorder) PeerConfig(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeerConfig", reflect.TypeOf((*MockConfig)(nil).PeerConfig), arg0, arg1) } // PeersConfig mocks base method -func (_m *MockConfig) PeersConfig(_param0 string) ([]apiconfig.PeerConfig, error) { - ret := _m.ctrl.Call(_m, "PeersConfig", _param0) +func (m *MockConfig) PeersConfig(arg0 string) ([]apiconfig.PeerConfig, error) { + ret := m.ctrl.Call(m, "PeersConfig", arg0) ret0, _ := ret[0].([]apiconfig.PeerConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // PeersConfig indicates an expected call of PeersConfig -func (_mr *MockConfigMockRecorder) PeersConfig(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "PeersConfig", arg0) +func (mr *MockConfigMockRecorder) PeersConfig(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeersConfig", reflect.TypeOf((*MockConfig)(nil).PeersConfig), arg0) } // RandomOrdererConfig mocks base method -func (_m *MockConfig) RandomOrdererConfig() (*apiconfig.OrdererConfig, error) { - ret := _m.ctrl.Call(_m, "RandomOrdererConfig") +func (m *MockConfig) RandomOrdererConfig() (*apiconfig.OrdererConfig, error) { + ret := m.ctrl.Call(m, "RandomOrdererConfig") ret0, _ := ret[0].(*apiconfig.OrdererConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // RandomOrdererConfig indicates an expected call of RandomOrdererConfig -func (_mr *MockConfigMockRecorder) RandomOrdererConfig() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "RandomOrdererConfig") +func (mr *MockConfigMockRecorder) RandomOrdererConfig() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RandomOrdererConfig", reflect.TypeOf((*MockConfig)(nil).RandomOrdererConfig)) } // SecurityAlgorithm mocks base method -func (_m *MockConfig) SecurityAlgorithm() string { - ret := _m.ctrl.Call(_m, "SecurityAlgorithm") +func (m *MockConfig) SecurityAlgorithm() string { + ret := m.ctrl.Call(m, "SecurityAlgorithm") ret0, _ := ret[0].(string) return ret0 } // SecurityAlgorithm indicates an expected call of SecurityAlgorithm -func (_mr *MockConfigMockRecorder) SecurityAlgorithm() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SecurityAlgorithm") +func (mr *MockConfigMockRecorder) SecurityAlgorithm() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecurityAlgorithm", reflect.TypeOf((*MockConfig)(nil).SecurityAlgorithm)) } // SecurityLevel mocks base method -func (_m *MockConfig) SecurityLevel() int { - ret := _m.ctrl.Call(_m, "SecurityLevel") +func (m *MockConfig) SecurityLevel() int { + ret := m.ctrl.Call(m, "SecurityLevel") ret0, _ := ret[0].(int) return ret0 } // SecurityLevel indicates an expected call of SecurityLevel -func (_mr *MockConfigMockRecorder) SecurityLevel() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SecurityLevel") +func (mr *MockConfigMockRecorder) SecurityLevel() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecurityLevel", reflect.TypeOf((*MockConfig)(nil).SecurityLevel)) } // SecurityProvider mocks base method -func (_m *MockConfig) SecurityProvider() string { - ret := _m.ctrl.Call(_m, "SecurityProvider") +func (m *MockConfig) SecurityProvider() string { + ret := m.ctrl.Call(m, "SecurityProvider") ret0, _ := ret[0].(string) return ret0 } // SecurityProvider indicates an expected call of SecurityProvider -func (_mr *MockConfigMockRecorder) SecurityProvider() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SecurityProvider") +func (mr *MockConfigMockRecorder) SecurityProvider() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecurityProvider", reflect.TypeOf((*MockConfig)(nil).SecurityProvider)) } // SecurityProviderLabel mocks base method -func (_m *MockConfig) SecurityProviderLabel() string { - ret := _m.ctrl.Call(_m, "SecurityProviderLabel") +func (m *MockConfig) SecurityProviderLabel() string { + ret := m.ctrl.Call(m, "SecurityProviderLabel") ret0, _ := ret[0].(string) return ret0 } // SecurityProviderLabel indicates an expected call of SecurityProviderLabel -func (_mr *MockConfigMockRecorder) SecurityProviderLabel() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SecurityProviderLabel") +func (mr *MockConfigMockRecorder) SecurityProviderLabel() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecurityProviderLabel", reflect.TypeOf((*MockConfig)(nil).SecurityProviderLabel)) } // SecurityProviderPin mocks base method -func (_m *MockConfig) SecurityProviderPin() string { - ret := _m.ctrl.Call(_m, "SecurityProviderPin") +func (m *MockConfig) SecurityProviderPin() string { + ret := m.ctrl.Call(m, "SecurityProviderPin") ret0, _ := ret[0].(string) return ret0 } // SecurityProviderPin indicates an expected call of SecurityProviderPin -func (_mr *MockConfigMockRecorder) SecurityProviderPin() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SecurityProviderPin") +func (mr *MockConfigMockRecorder) SecurityProviderPin() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecurityProviderPin", reflect.TypeOf((*MockConfig)(nil).SecurityProviderPin)) } // SetTLSCACertPool mocks base method -func (_m *MockConfig) SetTLSCACertPool(_param0 *x509.CertPool) { - _m.ctrl.Call(_m, "SetTLSCACertPool", _param0) +func (m *MockConfig) SetTLSCACertPool(arg0 *x509.CertPool) { + m.ctrl.Call(m, "SetTLSCACertPool", arg0) } // SetTLSCACertPool indicates an expected call of SetTLSCACertPool -func (_mr *MockConfigMockRecorder) SetTLSCACertPool(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SetTLSCACertPool", arg0) +func (mr *MockConfigMockRecorder) SetTLSCACertPool(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTLSCACertPool", reflect.TypeOf((*MockConfig)(nil).SetTLSCACertPool), arg0) } // SoftVerify mocks base method -func (_m *MockConfig) SoftVerify() bool { - ret := _m.ctrl.Call(_m, "SoftVerify") +func (m *MockConfig) SoftVerify() bool { + ret := m.ctrl.Call(m, "SoftVerify") ret0, _ := ret[0].(bool) return ret0 } // SoftVerify indicates an expected call of SoftVerify -func (_mr *MockConfigMockRecorder) SoftVerify() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "SoftVerify") +func (mr *MockConfigMockRecorder) SoftVerify() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SoftVerify", reflect.TypeOf((*MockConfig)(nil).SoftVerify)) } // TLSCACertPool mocks base method -func (_m *MockConfig) TLSCACertPool(_param0 string) (*x509.CertPool, error) { - ret := _m.ctrl.Call(_m, "TLSCACertPool", _param0) +func (m *MockConfig) TLSCACertPool(arg0 string) (*x509.CertPool, error) { + ret := m.ctrl.Call(m, "TLSCACertPool", arg0) ret0, _ := ret[0].(*x509.CertPool) ret1, _ := ret[1].(error) return ret0, ret1 } // TLSCACertPool indicates an expected call of TLSCACertPool -func (_mr *MockConfigMockRecorder) TLSCACertPool(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "TLSCACertPool", arg0) +func (mr *MockConfigMockRecorder) TLSCACertPool(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TLSCACertPool", reflect.TypeOf((*MockConfig)(nil).TLSCACertPool), arg0) } // TimeoutOrDefault mocks base method -func (_m *MockConfig) TimeoutOrDefault(_param0 apiconfig.ConnectionType) time.Duration { - ret := _m.ctrl.Call(_m, "TimeoutOrDefault", _param0) +func (m *MockConfig) TimeoutOrDefault(arg0 apiconfig.ConnectionType) time.Duration { + ret := m.ctrl.Call(m, "TimeoutOrDefault", arg0) ret0, _ := ret[0].(time.Duration) return ret0 } // TimeoutOrDefault indicates an expected call of TimeoutOrDefault -func (_mr *MockConfigMockRecorder) TimeoutOrDefault(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "TimeoutOrDefault", arg0) +func (mr *MockConfigMockRecorder) TimeoutOrDefault(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeoutOrDefault", reflect.TypeOf((*MockConfig)(nil).TimeoutOrDefault), arg0) } diff --git a/api/apiconfig/network.go b/api/apiconfig/network.go index ef2a0029f7..6dad56b69f 100644 --- a/api/apiconfig/network.go +++ b/api/apiconfig/network.go @@ -65,8 +65,14 @@ type PeerChannelConfig struct { EventSource bool } +type ChannelPeer struct { + PeerChannelConfig + PeerConfig +} + type OrganizationConfig struct { MspID string + CryptoPath string Peers []string CertificateAuthorities []string AdminPrivateKey TLSConfig diff --git a/api/apifabca/mocks/mockfabriccaclient.gen.go b/api/apifabca/mocks/mockfabriccaclient.gen.go index 27f818d609..86db17c347 100644 --- a/api/apifabca/mocks/mockfabriccaclient.gen.go +++ b/api/apifabca/mocks/mockfabriccaclient.gen.go @@ -1,12 +1,14 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/hyperledger/fabric-sdk-go/api/apifabca (interfaces: FabricCAClient) +// Package mock_apifabca is a generated GoMock package. package mock_apifabca import ( gomock "github.com/golang/mock/gomock" apifabca "github.com/hyperledger/fabric-sdk-go/api/apifabca" bccsp "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" + reflect "reflect" ) // MockFabricCAClient is a mock of FabricCAClient interface @@ -28,25 +30,25 @@ func NewMockFabricCAClient(ctrl *gomock.Controller) *MockFabricCAClient { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockFabricCAClient) EXPECT() *MockFabricCAClientMockRecorder { - return _m.recorder +func (m *MockFabricCAClient) EXPECT() *MockFabricCAClientMockRecorder { + return m.recorder } // CAName mocks base method -func (_m *MockFabricCAClient) CAName() string { - ret := _m.ctrl.Call(_m, "CAName") +func (m *MockFabricCAClient) CAName() string { + ret := m.ctrl.Call(m, "CAName") ret0, _ := ret[0].(string) return ret0 } // CAName indicates an expected call of CAName -func (_mr *MockFabricCAClientMockRecorder) CAName() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CAName") +func (mr *MockFabricCAClientMockRecorder) CAName() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CAName", reflect.TypeOf((*MockFabricCAClient)(nil).CAName)) } // Enroll mocks base method -func (_m *MockFabricCAClient) Enroll(_param0 string, _param1 string) (bccsp.Key, []byte, error) { - ret := _m.ctrl.Call(_m, "Enroll", _param0, _param1) +func (m *MockFabricCAClient) Enroll(arg0, arg1 string) (bccsp.Key, []byte, error) { + ret := m.ctrl.Call(m, "Enroll", arg0, arg1) ret0, _ := ret[0].(bccsp.Key) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) @@ -54,13 +56,13 @@ func (_m *MockFabricCAClient) Enroll(_param0 string, _param1 string) (bccsp.Key, } // Enroll indicates an expected call of Enroll -func (_mr *MockFabricCAClientMockRecorder) Enroll(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Enroll", arg0, arg1) +func (mr *MockFabricCAClientMockRecorder) Enroll(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enroll", reflect.TypeOf((*MockFabricCAClient)(nil).Enroll), arg0, arg1) } // Reenroll mocks base method -func (_m *MockFabricCAClient) Reenroll(_param0 apifabca.User) (bccsp.Key, []byte, error) { - ret := _m.ctrl.Call(_m, "Reenroll", _param0) +func (m *MockFabricCAClient) Reenroll(arg0 apifabca.User) (bccsp.Key, []byte, error) { + ret := m.ctrl.Call(m, "Reenroll", arg0) ret0, _ := ret[0].(bccsp.Key) ret1, _ := ret[1].([]byte) ret2, _ := ret[2].(error) @@ -68,31 +70,31 @@ func (_m *MockFabricCAClient) Reenroll(_param0 apifabca.User) (bccsp.Key, []byte } // Reenroll indicates an expected call of Reenroll -func (_mr *MockFabricCAClientMockRecorder) Reenroll(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Reenroll", arg0) +func (mr *MockFabricCAClientMockRecorder) Reenroll(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reenroll", reflect.TypeOf((*MockFabricCAClient)(nil).Reenroll), arg0) } // Register mocks base method -func (_m *MockFabricCAClient) Register(_param0 apifabca.User, _param1 *apifabca.RegistrationRequest) (string, error) { - ret := _m.ctrl.Call(_m, "Register", _param0, _param1) +func (m *MockFabricCAClient) Register(arg0 apifabca.User, arg1 *apifabca.RegistrationRequest) (string, error) { + ret := m.ctrl.Call(m, "Register", arg0, arg1) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // Register indicates an expected call of Register -func (_mr *MockFabricCAClientMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Register", arg0, arg1) +func (mr *MockFabricCAClientMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockFabricCAClient)(nil).Register), arg0, arg1) } // Revoke mocks base method -func (_m *MockFabricCAClient) Revoke(_param0 apifabca.User, _param1 *apifabca.RevocationRequest) error { - ret := _m.ctrl.Call(_m, "Revoke", _param0, _param1) +func (m *MockFabricCAClient) Revoke(arg0 apifabca.User, arg1 *apifabca.RevocationRequest) error { + ret := m.ctrl.Call(m, "Revoke", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // Revoke indicates an expected call of Revoke -func (_mr *MockFabricCAClientMockRecorder) Revoke(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Revoke", arg0, arg1) +func (mr *MockFabricCAClientMockRecorder) Revoke(arg0, arg1 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockFabricCAClient)(nil).Revoke), arg0, arg1) } diff --git a/api/apifabclient/credentialmgr.go b/api/apifabclient/credentialmgr.go new file mode 100644 index 0000000000..cd3333e0bd --- /dev/null +++ b/api/apifabclient/credentialmgr.go @@ -0,0 +1,22 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package apifabclient + +import "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" + +// SigningIdentity is the identity object that encapsulates the user's private key for signing +// and the user's enrollment certificate (identity) +type SigningIdentity struct { + MspID string + EnrollmentCert []byte + PrivateKey bccsp.Key +} + +// CredentialManager retrieves user's signing identity +type CredentialManager interface { + GetSigningIdentity(name string) (*SigningIdentity, error) +} diff --git a/api/apifabclient/discoveryprovider.go b/api/apifabclient/discoveryprovider.go new file mode 100644 index 0000000000..6bbd32cffe --- /dev/null +++ b/api/apifabclient/discoveryprovider.go @@ -0,0 +1,17 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package apifabclient + +// DiscoveryProvider is used to discover peers on the network +type DiscoveryProvider interface { + NewDiscoveryService(channel Channel) (DiscoveryService, error) +} + +// DiscoveryService is used to discover eligible peers on specific channel +type DiscoveryService interface { + GetPeers(chaincodeID string) ([]Peer, error) +} diff --git a/api/apifabclient/fabricclient.go b/api/apifabclient/fabricclient.go index 91ee77fdcc..0c873e2e42 100644 --- a/api/apifabclient/fabricclient.go +++ b/api/apifabclient/fabricclient.go @@ -38,9 +38,8 @@ type FabricClient interface { SignChannelConfig(config []byte) (*common.ConfigSignature, error) CreateChannel(request CreateChannelRequest) (txn.TransactionID, error) QueryChannelInfo(name string, peers []Peer) (Channel, error) - SetStateStore(stateStore KeyValueStore) StateStore() KeyValueStore - SetCryptoSuite(cryptoSuite bccsp.BCCSP) + SigningManager() SigningManager CryptoSuite() bccsp.BCCSP SaveUserToStateStore(user User, skipPersistence bool) error LoadUserFromStateStore(name string) (User, error) diff --git a/api/apifabclient/signingmgr.go b/api/apifabclient/signingmgr.go new file mode 100644 index 0000000000..e7619f0408 --- /dev/null +++ b/api/apifabclient/signingmgr.go @@ -0,0 +1,14 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package apifabclient + +import "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" + +// SigningManager signs object with provided key +type SigningManager interface { + Sign([]byte, bccsp.Key) ([]byte, error) +} diff --git a/api/apitxn/mocks/mockapitxn.gen.go b/api/apitxn/mocks/mockapitxn.gen.go index cb10897b52..cd52bac9c4 100644 --- a/api/apitxn/mocks/mockapitxn.gen.go +++ b/api/apitxn/mocks/mockapitxn.gen.go @@ -1,11 +1,13 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/hyperledger/fabric-sdk-go/api/apitxn (interfaces: ProposalProcessor) +// Package mock_apitxn is a generated GoMock package. package mock_apitxn import ( gomock "github.com/golang/mock/gomock" apitxn "github.com/hyperledger/fabric-sdk-go/api/apitxn" + reflect "reflect" ) // MockProposalProcessor is a mock of ProposalProcessor interface @@ -27,19 +29,19 @@ func NewMockProposalProcessor(ctrl *gomock.Controller) *MockProposalProcessor { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockProposalProcessor) EXPECT() *MockProposalProcessorMockRecorder { - return _m.recorder +func (m *MockProposalProcessor) EXPECT() *MockProposalProcessorMockRecorder { + return m.recorder } // ProcessTransactionProposal mocks base method -func (_m *MockProposalProcessor) ProcessTransactionProposal(_param0 apitxn.TransactionProposal) (apitxn.TransactionProposalResult, error) { - ret := _m.ctrl.Call(_m, "ProcessTransactionProposal", _param0) +func (m *MockProposalProcessor) ProcessTransactionProposal(arg0 apitxn.TransactionProposal) (apitxn.TransactionProposalResult, error) { + ret := m.ctrl.Call(m, "ProcessTransactionProposal", arg0) ret0, _ := ret[0].(apitxn.TransactionProposalResult) ret1, _ := ret[1].(error) return ret0, ret1 } // ProcessTransactionProposal indicates an expected call of ProcessTransactionProposal -func (_mr *MockProposalProcessorMockRecorder) ProcessTransactionProposal(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ProcessTransactionProposal", arg0) +func (mr *MockProposalProcessorMockRecorder) ProcessTransactionProposal(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProcessTransactionProposal", reflect.TypeOf((*MockProposalProcessor)(nil).ProcessTransactionProposal), arg0) } diff --git a/api/apitxn/txn.go b/api/apitxn/txn.go new file mode 100644 index 0000000000..6ad024fd83 --- /dev/null +++ b/api/apitxn/txn.go @@ -0,0 +1,105 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package apitxn + +import "time" + +// QueryRequest contains the parameters for query +type QueryRequest struct { + ChaincodeID string + Fcn string + Args [][]byte +} + +// QueryOpts allows the user to specify more advanced options +type QueryOpts struct { + Notifier chan QueryResponse // async + ProposalProcessors []ProposalProcessor // targets + Timeout time.Duration +} + +// QueryResponse contains result of asynchronous call +type QueryResponse struct { + Response []byte + Error error +} + +// ExecuteTxResponse contains result of asynchronous call +type ExecuteTxResponse struct { + Response TransactionID + Error error +} + +// ExecuteTxRequest contains the parameters to execute transaction +type ExecuteTxRequest struct { + ChaincodeID string + Fcn string + Args [][]byte + TransientMap map[string][]byte +} + +// ExecuteTxOpts allows the user to specify more advanced options +type ExecuteTxOpts struct { + Notifier chan ExecuteTxResponse // async + TxFilter ExecuteTxFilter + Timeout time.Duration +} + +// ExecuteTxFilter allows the user to inspect/modify response before commit +type ExecuteTxFilter interface { + // process transaction proposal response (there will be no commit if an error is returned) + ProcessTxProposalResponse(txProposalResponse []*TransactionProposalResponse) ([]*TransactionProposalResponse, error) +} + +// Registration is a handle that is returned from a successful Register Chaincode Event. +// This handle should be used in Unregister in order to unregister the event. +type Registration interface { +} + +// CCEvent contains the data for a chaincocde event +type CCEvent struct { + TxID string + ChaincodeID string + EventName string + Payload []byte +} + +// ChannelClient ... +/* + * A channel client instance provides a handler to interact with peers on specified channel. + * An application that requires interaction with multiple channels should create a separate + * instance of the channel client for each channel. Channel client supports non-admin functions only. + * + * Each Client instance maintains {@link Channel} instance representing channel and the associated + * private ledgers. + * + */ +type ChannelClient interface { + + // Query chaincode + Query(request QueryRequest) ([]byte, error) + + // QueryWithOpts allows the user to provide options for query (sync vs async, etc.) + QueryWithOpts(request QueryRequest, opt QueryOpts) ([]byte, error) + + // ExecuteTx execute transaction + ExecuteTx(request ExecuteTxRequest) (TransactionID, error) + + // ExecuteTxWithOpts allows the user to provide options for transaction execution (sync vs async, etc.) + ExecuteTxWithOpts(request ExecuteTxRequest, opt ExecuteTxOpts) (TransactionID, error) + + // RegisterChaincodeEvent registers chain code event + // @param {chan bool} channel which receives event details when the event is complete + // @returns {object} object handle that should be used to unregister + RegisterChaincodeEvent(notify chan<- *CCEvent, chainCodeID string, eventID string) Registration + + // UnregisterChaincodeEvent unregisters chain code event + UnregisterChaincodeEvent(registration Registration) error + + // Close releases channel client resources (disconnects event hub etc.) + Close() error +} diff --git a/def/fabapi/context/context.go b/def/fabapi/context/context.go index a61940edda..49e7073bbd 100644 --- a/def/fabapi/context/context.go +++ b/def/fabapi/context/context.go @@ -17,6 +17,8 @@ type SDK interface { CryptoSuiteProvider() bccsp.BCCSP StateStoreProvider() fab.KeyValueStore ConfigProvider() apiconfig.Config + DiscoveryProvider() fab.DiscoveryProvider + SigningManager() fab.SigningManager } // Org represents the organization context diff --git a/def/fabapi/context/defprovider/org.go b/def/fabapi/context/defprovider/org.go index 2bb0e54bc7..fde2c8089e 100644 --- a/def/fabapi/context/defprovider/org.go +++ b/def/fabapi/context/defprovider/org.go @@ -11,7 +11,10 @@ import ( "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fabca "github.com/hyperledger/fabric-sdk-go/api/apifabca" + fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" fabricCAClient "github.com/hyperledger/fabric-sdk-go/pkg/fabric-ca-client" + credentialMgr "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/credentialmgr" + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" ) // OrgClientFactory represents the default org provider factory. @@ -32,3 +35,14 @@ func (f *OrgClientFactory) NewMSPClient(orgName string, config apiconfig.Config) return mspClient, nil } + +// NewCredentialManager returns a new default implmentation of the credential manager +func (f *OrgClientFactory) NewCredentialManager(orgName string, config apiconfig.Config, cryptoProvider bccsp.BCCSP) (fab.CredentialManager, error) { + + credentialMgr, err := credentialMgr.NewCredentialManager(orgName, config, cryptoProvider) + if err != nil { + return nil, fmt.Errorf("NewCredentialManager returned error: %v", err) + } + + return credentialMgr, nil +} diff --git a/def/fabapi/context/defprovider/sdk.go b/def/fabapi/context/defprovider/sdk.go index 23d9811a9a..a351a91ad4 100644 --- a/def/fabapi/context/defprovider/sdk.go +++ b/def/fabapi/context/defprovider/sdk.go @@ -14,6 +14,9 @@ import ( "github.com/hyperledger/fabric-sdk-go/def/fabapi/opt" configImpl "github.com/hyperledger/fabric-sdk-go/pkg/config" kvs "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/keyvaluestore" + + signingMgr "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/signingmgr" + discovery "github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/discovery" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" bccspFactory "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" ) @@ -45,3 +48,14 @@ func (f *DefaultProviderFactory) NewStateStoreProvider(o opt.StateStoreOpts, con func (f *DefaultProviderFactory) NewCryptoSuiteProvider(config *bccspFactory.FactoryOpts) (bccsp.BCCSP, error) { return bccspFactory.GetBCCSPFromOpts(config) } + +// NewSigningManager returns a new default implementation of signing manager +func (f *DefaultProviderFactory) NewSigningManager(cryptoProvider bccsp.BCCSP, config apiconfig.Config) (fab.SigningManager, error) { + return signingMgr.NewSigningManager(cryptoProvider, config) +} + +// NewDiscoveryProvider returns a new default implementation of discovery provider +func (f *DefaultProviderFactory) NewDiscoveryProvider(config apiconfig.Config) (fab.DiscoveryProvider, error) { + return discovery.NewDiscoveryProvider(config) + +} diff --git a/def/fabapi/context/defprovider/session.go b/def/fabapi/context/defprovider/session.go index 40cc2cd0d9..bf0648c122 100644 --- a/def/fabapi/context/defprovider/session.go +++ b/def/fabapi/context/defprovider/session.go @@ -7,10 +7,16 @@ SPDX-License-Identifier: Apache-2.0 package defprovider import ( + "fmt" + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/api/apitxn" "github.com/hyperledger/fabric-sdk-go/def/fabapi/context" clientImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/events" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/orderer" + chImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/chclient" ) // SessionClientFactory represents the default implementation of a session client. @@ -29,6 +35,107 @@ func (f *SessionClientFactory) NewSystemClient(sdk context.SDK, session context. client.SetCryptoSuite(sdk.CryptoSuiteProvider()) client.SetStateStore(sdk.StateStoreProvider()) client.SetUserContext(session.Identity()) + client.SetSigningManager(sdk.SigningManager()) return client, nil } + +// NewChannelClient returns a client that can execute transactions on specified channel +func (f *SessionClientFactory) NewChannelClient(sdk context.SDK, session context.Session, config apiconfig.Config, channelName string) (apitxn.ChannelClient, error) { + + client := clientImpl.NewClient(sdk.ConfigProvider()) + client.SetCryptoSuite(sdk.CryptoSuiteProvider()) + client.SetStateStore(sdk.StateStoreProvider()) + client.SetUserContext(session.Identity()) + client.SetSigningManager(sdk.SigningManager()) + + channel, err := getChannel(client, channelName) + if err != nil { + return nil, fmt.Errorf("Unable to create channel:%v", err) + } + + discovery, err := sdk.DiscoveryProvider().NewDiscoveryService(channel) + if err != nil { + return nil, fmt.Errorf("Unable to create discovery service:%v", err) + } + + eventHub, err := getEventHub(client, channelName) + if err != nil { + return nil, err + } + + return chImpl.NewChannelClient(client, channel, discovery, eventHub) +} + +// getChannel is helper method to initializes and returns a channel based on config +func getChannel(client fab.FabricClient, channelID string) (fab.Channel, error) { + + channel, err := client.NewChannel(channelID) + if err != nil { + return nil, fmt.Errorf("NewChannel return error: %v", err) + } + + chConfig, err := client.Config().ChannelConfig(channel.Name()) + if err != nil { + return nil, fmt.Errorf("Error reading channel config: %v", err) + } + + for _, name := range chConfig.Orderers { + ordererConfig, err := client.Config().OrdererConfig(name) + if err != nil { + return nil, fmt.Errorf("Unable to retrieve configuration for orderer(%s): %s", name, err) + } + + serverHostOverride := "" + if str, ok := ordererConfig.GrpcOptions["ssl-target-name-override"].(string); ok { + serverHostOverride = str + } + orderer, err := orderer.NewOrderer(ordererConfig.URL, ordererConfig.TlsCACerts.Path, serverHostOverride, client.Config()) + if err != nil { + return nil, fmt.Errorf("NewOrderer return error: %v", err) + } + err = channel.AddOrderer(orderer) + if err != nil { + return nil, fmt.Errorf("Error adding orderer: %v", err) + } + } + + return channel, nil +} + +func getEventHub(client fab.FabricClient, channelID string) (*events.EventHub, error) { + + peerConfig, err := client.Config().ChannelPeers(channelID) + if err != nil { + return nil, fmt.Errorf("Unable to read configuration for channel(%s) peers: %s", channelID, err) + } + + serverHostOverride := "" + var eventSource *apiconfig.PeerConfig + + for _, p := range peerConfig { + + if p.EventSource { + serverHostOverride = "" + if str, ok := p.GrpcOptions["ssl-target-name-override"].(string); ok { + serverHostOverride = str + } + eventSource = &p.PeerConfig + break + } + } + + if eventSource == nil { + return nil, fmt.Errorf("Unable to find peer event source for channel: %s", channelID) + } + + // Event source found create event hub + eventHub, err := events.NewEventHub(client) + if err != nil { + return nil, err + } + + eventHub.SetPeerAddr(eventSource.EventUrl, eventSource.TlsCACerts.Path, serverHostOverride) + + return eventHub, nil +} diff --git a/def/fabapi/context/provider.go b/def/fabapi/context/provider.go index 2464981518..139246dcd9 100644 --- a/def/fabapi/context/provider.go +++ b/def/fabapi/context/provider.go @@ -10,6 +10,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fabca "github.com/hyperledger/fabric-sdk-go/api/apifabca" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + txn "github.com/hyperledger/fabric-sdk-go/api/apitxn" "github.com/hyperledger/fabric-sdk-go/def/fabapi/opt" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" bccspFactory "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" @@ -20,16 +21,19 @@ type SDKProviderFactory interface { NewConfigProvider(o opt.ConfigOpts, a opt.SDKOpts) (apiconfig.Config, error) NewStateStoreProvider(o opt.StateStoreOpts, config apiconfig.Config) (fab.KeyValueStore, error) NewCryptoSuiteProvider(config *bccspFactory.FactoryOpts) (bccsp.BCCSP, error) + NewSigningManager(cryptoProvider bccsp.BCCSP, config apiconfig.Config) (fab.SigningManager, error) + NewDiscoveryProvider(config apiconfig.Config) (fab.DiscoveryProvider, error) } // OrgClientFactory allows overriding default clients and providers of an organization // Currently, a context is created for each organization that the client app needs. type OrgClientFactory interface { NewMSPClient(orgName string, config apiconfig.Config) (fabca.FabricCAClient, error) + NewCredentialManager(orgName string, config apiconfig.Config, cryptoProvider bccsp.BCCSP) (fab.CredentialManager, error) } // SessionClientFactory allows overriding default clients and providers of a session type SessionClientFactory interface { NewSystemClient(context SDK, session Session, config apiconfig.Config) (fab.FabricClient, error) - //NewChannelClient(session Session) fab.Channel + NewChannelClient(context SDK, session Session, config apiconfig.Config, channelName string) (txn.ChannelClient, error) } diff --git a/def/fabapi/fabapi.go b/def/fabapi/fabapi.go index eaac8f2786..49392c3d7d 100644 --- a/def/fabapi/fabapi.go +++ b/def/fabapi/fabapi.go @@ -14,6 +14,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/api/apiconfig" "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/api/apitxn" "github.com/hyperledger/fabric-sdk-go/def/fabapi/context" "github.com/hyperledger/fabric-sdk-go/def/fabapi/context/defprovider" "github.com/hyperledger/fabric-sdk-go/def/fabapi/opt" @@ -42,9 +43,17 @@ type FabricSDK struct { Options // Implementations of client functionality (defaults are used if not specified) - configProvider apiconfig.Config - stateStore apifabclient.KeyValueStore - cryptoSuite bccsp.BCCSP // TODO - maybe copy this interface into the API package + configProvider apiconfig.Config + stateStore apifabclient.KeyValueStore + cryptoSuite bccsp.BCCSP // TODO - maybe copy this interface into the API package + discoveryProvider apifabclient.DiscoveryProvider + signingManager apifabclient.SigningManager +} + +// ChannelClientOpts provides options for creating channel client +type ChannelClientOpts struct { + OrgName string + ConfigProvider apiconfig.Config } // NewSDK initializes default clients @@ -90,6 +99,20 @@ func NewSDK(options Options) (*FabricSDK, error) { } sdk.stateStore = store + // Initialize discovery provider + discoveryProvider, err := sdk.ProviderFactory.NewDiscoveryProvider(sdk.configProvider) + if err != nil { + return nil, fmt.Errorf("Failed to initialize discovery provider [%s]", err) + } + sdk.discoveryProvider = discoveryProvider + + // Initialize Signing Manager + signingMgr, err := sdk.ProviderFactory.NewSigningManager(sdk.CryptoSuiteProvider(), sdk.configProvider) + if err != nil { + return nil, fmt.Errorf("Failed to initialize signing manager [%s]", err) + } + sdk.signingManager = signingMgr + return &sdk, nil } @@ -108,6 +131,16 @@ func (sdk *FabricSDK) StateStoreProvider() apifabclient.KeyValueStore { return sdk.stateStore } +// DiscoveryProvider returns discovery provider +func (sdk *FabricSDK) DiscoveryProvider() apifabclient.DiscoveryProvider { + return sdk.discoveryProvider +} + +// SigningManager returns signing manager +func (sdk *FabricSDK) SigningManager() apifabclient.SigningManager { + return sdk.signingManager +} + // NewContext creates a context from an org func (sdk *FabricSDK) NewContext(orgID string) (*OrgContext, error) { return NewOrgContext(sdk.ContextFactory, orgID, sdk.configProvider) @@ -126,14 +159,93 @@ func (sdk *FabricSDK) NewSystemClient(s context.Session) (apifabclient.FabricCli client.SetCryptoSuite(sdk.cryptoSuite) client.SetStateStore(sdk.stateStore) + client.SetUserContext(s.Identity()) + client.SetSigningManager(sdk.signingManager) return client, nil } -/* -TODO -// NewChannelClient returns a new client for a channel. -func (sdk *FabricSDK) NewChannelClient(s Session) apifabclient.Channel { - return nil +// NewChannelClient returns a new client for a channel +func (sdk *FabricSDK) NewChannelClient(channelName string, userName string) (apitxn.ChannelClient, error) { + + // Read default org name from configuration + client, err := sdk.configProvider.Client() + if err != nil { + return nil, fmt.Errorf("Unable to retrieve client from network config: %s", err) + } + + if client.Organization == "" { + return nil, fmt.Errorf("Must provide default organisation name in configuration") + } + + opt := &ChannelClientOpts{OrgName: client.Organization, ConfigProvider: sdk.configProvider} + + return sdk.NewChannelClientWithOpts(channelName, userName, opt) +} + +// NewChannelClientWithOpts returns a new client for a channel (user has to be pre-enrolled) +func (sdk *FabricSDK) NewChannelClientWithOpts(channelName string, userName string, opt *ChannelClientOpts) (apitxn.ChannelClient, error) { + + if opt == nil || opt.OrgName == "" { + return nil, fmt.Errorf("Organization name must be provided") + } + + session, err := sdk.NewPreEnrolledUserSession(opt.OrgName, userName) + if err != nil { + return nil, fmt.Errorf("Error getting pre-enrolled user session: %v", err) + } + + configProvider := sdk.ConfigProvider() + if opt.ConfigProvider != nil { + configProvider = opt.ConfigProvider + } + + client, err := sdk.SessionFactory.NewChannelClient(sdk, session, configProvider, channelName) + if err != nil { + return nil, fmt.Errorf("NewChannelClient returned error: %v", err) + } + + return client, nil +} + +// NewPreEnrolledUser returns a new pre-enrolled user +func (sdk *FabricSDK) NewPreEnrolledUser(orgID string, userName string) (apifabclient.User, error) { + + credentialMgr, err := sdk.ContextFactory.NewCredentialManager(orgID, sdk.ConfigProvider(), sdk.CryptoSuiteProvider()) + if err != nil { + return nil, fmt.Errorf("Error getting credential manager: %s ", err) + } + + signingIdentity, err := credentialMgr.GetSigningIdentity(userName) + if err != nil { + return nil, fmt.Errorf("Error getting signing identity: %s ", err) + } + + user, err := NewPreEnrolledUser(sdk.ConfigProvider(), userName, signingIdentity) + if err != nil { + return nil, fmt.Errorf("NewUser returned error: %v", err) + } + + return user, nil +} + +// NewPreEnrolledUserSession returns a new pre-enrolled user session +func (sdk *FabricSDK) NewPreEnrolledUserSession(orgID string, userName string) (*Session, error) { + + context, err := sdk.NewContext(orgID) + if err != nil { + return nil, fmt.Errorf("Error getting a context for org: %s", err) + } + + user, err := sdk.NewPreEnrolledUser(orgID, userName) + if err != nil { + return nil, fmt.Errorf("Error getting pre-enrolled user: %v", err) + } + + session, err := sdk.NewSession(context, user) + if err != nil { + return nil, fmt.Errorf("NewSession returned error: %v", err) + } + + return session, nil } -*/ diff --git a/def/fabapi/fabapi_test.go b/def/fabapi/fabapi_test.go index 2636ecd465..1116dfb86c 100644 --- a/def/fabapi/fabapi_test.go +++ b/def/fabapi/fabapi_test.go @@ -16,15 +16,19 @@ func TestNewDefaultSDK(t *testing.T) { setup := Options{ ConfigFile: "../../test/fixtures/config/config_test.yaml", - // OrgID: "org1", StateStoreOpts: opt.StateStoreOpts{ Path: "/tmp/state", }, } - _, err := NewSDK(setup) + sdk, err := NewSDK(setup) if err != nil { t.Fatalf("Error initializing SDK: %s", err) } + _, err = sdk.NewChannelClient("mychannel", "User1") + if err != nil { + t.Fatalf("Failed to create new channel client: %s", err) + } + } diff --git a/def/fabapi/pkgfactory.go b/def/fabapi/pkgfactory.go index 51dab3219a..062f6d0e03 100644 --- a/def/fabapi/pkgfactory.go +++ b/def/fabapi/pkgfactory.go @@ -8,12 +8,10 @@ package fabapi import ( "fmt" - "io/ioutil" config "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fabca "github.com/hyperledger/fabric-sdk-go/api/apifabca" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" - fabricCaUtil "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util" configImpl "github.com/hyperledger/fabric-sdk-go/pkg/config" fabricCAClient "github.com/hyperledger/fabric-sdk-go/pkg/fabric-ca-client" clientImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client" @@ -22,6 +20,7 @@ import ( kvs "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/keyvaluestore" ordererImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/orderer" peerImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/signingmgr" bccsp "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" bccspFactory "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" ) @@ -46,6 +45,13 @@ func NewClient(user fabca.User, skipUserPersistence bool, stateStorePath string, } client.SaveUserToStateStore(user, skipUserPersistence) + signingMgr, err := signingmgr.NewSigningManager(cryptosuite, config) + if err != nil { + return nil, fmt.Errorf("NewSigningManager returned error[%s]", err) + } + + client.SetSigningManager(signingMgr) + return client, nil } @@ -83,39 +89,6 @@ func NewClientWithUser(name string, pwd string, orgName string, return client, nil } -// NewClientWithPreEnrolledUser returns a new default Client implementation -// by using a the default implementation of a pre-enrolled user. -func NewClientWithPreEnrolledUser(config config.Config, stateStorePath string, - skipUserPersistence bool, username string, keyDir string, certDir string, - orgName string, cryptosuite bccsp.BCCSP) (fab.FabricClient, error) { - - client := clientImpl.NewClient(config) - - if cryptosuite == nil { - cryptosuite = bccspFactory.GetDefault() - } - client.SetCryptoSuite(cryptosuite) - if stateStorePath != "" { - stateStore, err := kvs.CreateNewFileKeyValueStore(stateStorePath) - if err != nil { - return nil, fmt.Errorf("CreateNewFileKeyValueStore returned error[%s]", err) - } - client.SetStateStore(stateStore) - } - mspID, err := client.Config().MspID(orgName) - if err != nil { - return nil, fmt.Errorf("Error reading MSP ID config: %s", err) - } - user, err := NewPreEnrolledUser(client.Config(), keyDir, certDir, username, mspID, client.CryptoSuite()) - if err != nil { - return nil, fmt.Errorf("NewPreEnrolledUser returned error: %v", err) - } - client.SetUserContext(user) - client.SaveUserToStateStore(user, skipUserPersistence) - - return client, nil -} - // NewUser returns a new default implementation of a User. func NewUser(config config.Config, msp fabca.FabricCAClient, name string, pwd string, mspID string) (fabca.User, error) { @@ -131,22 +104,13 @@ func NewUser(config config.Config, msp fabca.FabricCAClient, name string, pwd st return user, nil } -// NewPreEnrolledUser returns a new default implementation of User. -// The user should already be pre-enrolled. -func NewPreEnrolledUser(config config.Config, privateKeyPath string, - enrollmentCertPath string, username string, mspID string, cryptoSuite bccsp.BCCSP) (fabca.User, error) { - privateKey, err := fabricCaUtil.ImportBCCSPKeyFromPEM(privateKeyPath, cryptoSuite, true) - if err != nil { - return nil, fmt.Errorf("Error importing private key: %v", err) - } - enrollmentCert, err := ioutil.ReadFile(enrollmentCertPath) - if err != nil { - return nil, fmt.Errorf("Error reading from the enrollment cert path: %v", err) - } +// NewPreEnrolledUser returns a new default implementation of a User. +func NewPreEnrolledUser(config config.Config, name string, signingIdentity *fab.SigningIdentity) (fabca.User, error) { + + user := identityImpl.NewUser(name, signingIdentity.MspID) - user := identityImpl.NewUser(username, mspID) - user.SetEnrollmentCertificate(enrollmentCert) - user.SetPrivateKey(privateKey) + user.SetPrivateKey(signingIdentity.PrivateKey) + user.SetEnrollmentCertificate(signingIdentity.EnrollmentCert) return user, nil } @@ -175,7 +139,7 @@ func NewChannel(client fab.FabricClient, orderer fab.Orderer, peers []fab.Peer, } // NewSystemClient returns a new default implementation of Client -func NewSystemClient(config config.Config) fab.FabricClient { +func NewSystemClient(config config.Config) *clientImpl.Client { return clientImpl.NewClient(config) } @@ -193,6 +157,11 @@ func NewCryptoSuite(config *bccspFactory.FactoryOpts) (bccsp.BCCSP, error) { return bccspFactory.GetBCCSPFromOpts(config) } +// NewSigningManager returns a new default implementation of signing manager +func NewSigningManager(cryptoProvider bccsp.BCCSP, config config.Config) (fab.SigningManager, error) { + return signingmgr.NewSigningManager(cryptoProvider, config) +} + // NewEventHub returns a new default implementation of EventHub func NewEventHub(client fab.FabricClient) (fab.EventHub, error) { return eventsImpl.NewEventHub(client) diff --git a/pkg/config/config.go b/pkg/config/config.go index 41e64d5f53..733519ed46 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -129,7 +129,7 @@ func (c *Config) CAConfig(org string) (*apiconfig.CAConfig, error) { if err != nil { return nil, err } - caConfig := config.CertificateAuthorities[caName] + caConfig := config.CertificateAuthorities[strings.ToLower(caName)] return &caConfig, nil } @@ -144,7 +144,7 @@ func (c *Config) CAServerCertFiles(org string) ([]string, error) { if err != nil { return nil, err } - if _, ok := config.CertificateAuthorities[caName]; !ok { + if _, ok := config.CertificateAuthorities[strings.ToLower(caName)]; !ok { return nil, fmt.Errorf("CA Server Name '%s' not found", caName) } certFiles := strings.Split(config.CertificateAuthorities[caName].TlsCACerts.Path, ",") @@ -164,11 +164,11 @@ func (c *Config) getCAName(org string) (string, error) { logger.Debug("Getting cert authority for org: %s.", org) - if len(config.Organizations[org].CertificateAuthorities) == 0 { + if len(config.Organizations[strings.ToLower(org)].CertificateAuthorities) == 0 { return "", fmt.Errorf("organization %s has no Certificate Authorities setup. Make sure each org has at least 1 configured", org) } //for now, we're only loading the first Cert Authority by default. TODO add logic to support passing the Cert Authority ID needed by the client. - certAuthorityName := config.Organizations[org].CertificateAuthorities[0] + certAuthorityName := config.Organizations[strings.ToLower(org)].CertificateAuthorities[0] logger.Debugf("Cert authority for org: %s is %s", org, certAuthorityName) if certAuthorityName == "" { @@ -188,10 +188,10 @@ func (c *Config) CAClientKeyFile(org string) (string, error) { if err != nil { return "", err } - if _, ok := config.CertificateAuthorities[caName]; !ok { + if _, ok := config.CertificateAuthorities[strings.ToLower(caName)]; !ok { return "", fmt.Errorf("CA Server Name '%s' not found", caName) } - return strings.Replace(config.CertificateAuthorities[caName].TlsCACerts.Client.Keyfile, + return strings.Replace(config.CertificateAuthorities[strings.ToLower(caName)].TlsCACerts.Client.Keyfile, "$GOPATH", os.Getenv("GOPATH"), -1), nil } @@ -206,10 +206,10 @@ func (c *Config) CAClientCertFile(org string) (string, error) { if err != nil { return "", err } - if _, ok := config.CertificateAuthorities[caName]; !ok { + if _, ok := config.CertificateAuthorities[strings.ToLower(caName)]; !ok { return "", fmt.Errorf("CA Server Name '%s' not found", caName) } - return strings.Replace(config.CertificateAuthorities[caName].TlsCACerts.Client.Certfile, + return strings.Replace(config.CertificateAuthorities[strings.ToLower(caName)].TlsCACerts.Client.Certfile, "$GOPATH", os.Getenv("GOPATH"), -1), nil } @@ -240,7 +240,7 @@ func (c *Config) MspID(org string) (string, error) { return "", err } // viper lowercases all key maps, org is lower case - mspID := config.Organizations[org].MspID + mspID := config.Organizations[strings.ToLower(org)].MspID if mspID == "" { return "", fmt.Errorf("MSP ID is empty for org: %s", org) } @@ -348,7 +348,7 @@ func (c *Config) OrdererConfig(name string) (*apiconfig.OrdererConfig, error) { if err != nil { return nil, err } - orderer := config.Orderers[name] + orderer := config.Orderers[strings.ToLower(name)] if orderer.TlsCACerts.Path != "" { orderer.TlsCACerts.Path = strings.Replace(orderer.TlsCACerts.Path, "$GOPATH", os.Getenv("GOPATH"), -1) @@ -365,19 +365,13 @@ func (c *Config) PeersConfig(org string) ([]apiconfig.PeerConfig, error) { return nil, err } - peersConfig := config.Organizations[org].Peers + peersConfig := config.Organizations[strings.ToLower(org)].Peers peers := []apiconfig.PeerConfig{} for _, peerName := range peersConfig { - p := config.Peers[peerName] - if p.Url == "" { - return nil, fmt.Errorf("URL does not exist or empty for peer %s", peerName) - } - if p.EventUrl == "" { - return nil, fmt.Errorf("Event URL does not exist or empty for peer %s", peerName) - } - if c.IsTLSEnabled() && p.TlsCACerts.Pem == "" && p.TlsCACerts.Path == "" { - return nil, fmt.Errorf("tls.certificate does not exist or empty for peer %s", peerName) + p := config.Peers[strings.ToLower(peerName)] + if err = verifyPeerConfig(p, peerName, c.IsTLSEnabled()); err != nil { + return nil, err } if p.TlsCACerts.Path != "" { p.TlsCACerts.Path = strings.Replace(p.TlsCACerts.Path, "$GOPATH", @@ -396,7 +390,7 @@ func (c *Config) PeerConfig(org string, name string) (*apiconfig.PeerConfig, err return nil, err } - peersConfig := config.Organizations[org].Peers + peersConfig := config.Organizations[strings.ToLower(org)].Peers peerInOrg := false for _, p := range peersConfig { if p == name { @@ -406,7 +400,7 @@ func (c *Config) PeerConfig(org string, name string) (*apiconfig.PeerConfig, err if !peerInOrg { return nil, fmt.Errorf("Peer %s is not part of orgianzation %s", name, org) } - peerConfig := config.Peers[name] + peerConfig := config.Peers[strings.ToLower(name)] if peerConfig.TlsCACerts.Path != "" { peerConfig.TlsCACerts.Path = strings.Replace(peerConfig.TlsCACerts.Path, "$GOPATH", os.Getenv("GOPATH"), -1) @@ -426,6 +420,75 @@ func (c *Config) NetworkConfig() (*apiconfig.NetworkConfig, error) { return c.networkConfig, nil } +// ChannelConfig returns the channel configuration +func (c *Config) ChannelConfig(name string) (*apiconfig.ChannelConfig, error) { + config, err := c.NetworkConfig() + if err != nil { + return nil, err + } + + // viper lowercases all key maps + ch, ok := config.Channels[strings.ToLower(name)] + if !ok { + return nil, fmt.Errorf("Channel config not found for %s", name) + } + + return &ch, nil +} + +// ChannelPeers returns the channel peers configuration +func (c *Config) ChannelPeers(name string) ([]apiconfig.ChannelPeer, error) { + netConfig, err := c.NetworkConfig() + if err != nil { + return nil, err + } + + // viper lowercases all key maps + chConfig, ok := netConfig.Channels[strings.ToLower(name)] + if !ok { + return nil, fmt.Errorf("Channel config not found for %s", name) + } + + peers := []apiconfig.ChannelPeer{} + + for peerName, chPeerConfig := range chConfig.Peers { + + // Get generic peer configuration + p, ok := netConfig.Peers[strings.ToLower(peerName)] + if !ok { + return nil, fmt.Errorf("Peer config not found for %s", peerName) + } + + if err = verifyPeerConfig(p, peerName, c.IsTLSEnabled()); err != nil { + return nil, err + } + + if p.TlsCACerts.Path != "" { + p.TlsCACerts.Path = strings.Replace(p.TlsCACerts.Path, "$GOPATH", os.Getenv("GOPATH"), -1) + } + + peer := apiconfig.ChannelPeer{PeerChannelConfig: chPeerConfig, PeerConfig: p} + + peers = append(peers, peer) + } + + return peers, nil + +} + +func verifyPeerConfig(p apiconfig.PeerConfig, peerName string, tlsEnabled bool) error { + if p.Url == "" { + return fmt.Errorf("URL does not exist or empty for peer %s", peerName) + } + if p.EventUrl == "" { + return fmt.Errorf("Event URL does not exist or empty for peer %s", peerName) + } + if tlsEnabled && p.TlsCACerts.Pem == "" && p.TlsCACerts.Path == "" { + return fmt.Errorf("tls.certificate does not exist or empty for peer %s", peerName) + } + return nil +} + // IsTLSEnabled is TLS enabled? func (c *Config) IsTLSEnabled() bool { return myViper.GetBool("client.tls.enabled") diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 3caff9e2a2..e8aa23ad72 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -22,7 +22,7 @@ import ( var configImpl api.Config var org0 = "org0" -var org1 = "org1" +var org1 = "Org1" func TestDefaultConfig(t *testing.T) { vConfig := viper.New() @@ -69,7 +69,7 @@ func TestCAConfig(t *testing.T) { } //Test client organization - if vConfig.GetString("client.organization") != "org1" { + if vConfig.GetString("client.organization") != org1 { t.Fatalf("Incorrect Client organization") } @@ -77,34 +77,34 @@ func TestCAConfig(t *testing.T) { crossCheckWithViperConfig(myViper.GetString("client.cryptoconfig.path"), configImpl.CryptoConfigPath(), "Incorrect crypto config path", t) //Testing CA Client File Location - certfile, err := configImpl.CAClientCertFile("org1") + certfile, err := configImpl.CAClientCertFile(org1) if certfile == "" || err != nil { t.Fatalf("CA Cert file location read failed %s", err) } //Testing CA Key File Location - keyFile, err := configImpl.CAClientKeyFile("org1") + keyFile, err := configImpl.CAClientKeyFile(org1) if keyFile == "" || err != nil { t.Fatal("CA Key file location read failed") } //Testing CA Server Cert Files - sCertFiles, err := configImpl.CAServerCertFiles("org1") + sCertFiles, err := configImpl.CAServerCertFiles(org1) if sCertFiles == nil || len(sCertFiles) == 0 || err != nil { t.Fatal("Getting CA server cert files failed") } //Testing MSPID - mspID, err := configImpl.MspID("org1") + mspID, err := configImpl.MspID(org1) if mspID != "Org1MSP" || err != nil { t.Fatal("Get MSP ID failed") } //Testing CAConfig - caConfig, err := configImpl.CAConfig("org1") + caConfig, err := configImpl.CAConfig(org1) if caConfig == nil || err != nil { t.Fatal("Get CA Config failed") } @@ -280,7 +280,7 @@ func TestCAConfigFailsByNetworkConfig(t *testing.T) { func TestTLSACAConfig(t *testing.T) { //Test TLSCA Cert Pool (Positive test case) - certFile, _ := configImpl.CAClientCertFile("org1") + certFile, _ := configImpl.CAClientCertFile(org1) _, err := configImpl.TLSCACertPool(certFile) if err != nil { t.Fatalf("TLS CA cert pool fetch failed, reason: %v", err) @@ -292,7 +292,7 @@ func TestTLSACAConfig(t *testing.T) { t.Fatalf("TLS CA cert pool was supposed to fail") } - keyFile, _ := configImpl.CAClientKeyFile("org1") + keyFile, _ := configImpl.CAClientKeyFile(org1) _, err = configImpl.TLSCACertPool(keyFile) if err == nil { t.Fatalf("TLS CA cert pool was supposed to fail when provided with wrong cert file") @@ -580,7 +580,8 @@ func TestNetworkConfig(t *testing.T) { if len(conf.Organizations) == 0 { t.Fatal("Expected atleast one organisation to be set") } - if len(conf.Organizations[org1].Peers) == 0 { + // viper map keys are lowercase + if len(conf.Organizations[strings.ToLower(org1)].Peers) == 0 { t.Fatalf("Expected org %s to be present in network configuration and peers to be set", org1) } } diff --git a/pkg/fabric-ca-client/mocks/mockconfig.go b/pkg/fabric-ca-client/mocks/mockconfig.go index 2e024efb4e..c1a6f5c30e 100644 --- a/pkg/fabric-ca-client/mocks/mockconfig.go +++ b/pkg/fabric-ca-client/mocks/mockconfig.go @@ -147,6 +147,16 @@ func (c *MockConfig) NetworkConfig() (*apiconfig.NetworkConfig, error) { return nil, nil } +// ChannelConfig returns the channel configuration +func (c *MockConfig) ChannelConfig(name string) (*apiconfig.ChannelConfig, error) { + return nil, nil +} + +// ChannelPeers returns the channel peers configuration +func (c *MockConfig) ChannelPeers(name string) ([]apiconfig.ChannelPeer, error) { + return nil, nil +} + //SecurityProvider provider SW or PKCS11 func (c *MockConfig) SecurityProvider() string { return "SW" diff --git a/pkg/fabric-client/channel/channel.go b/pkg/fabric-client/channel/channel.go index 8ef7df94b4..a28af9ab07 100644 --- a/pkg/fabric-client/channel/channel.go +++ b/pkg/fabric-client/channel/channel.go @@ -12,8 +12,7 @@ import ( fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" "github.com/hyperledger/fabric-sdk-go/api/apitxn" "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" - "github.com/op/go-logging" + logging "github.com/op/go-logging" ) var logger = logging.MustGetLogger("fabric_sdk_go") @@ -34,7 +33,7 @@ type Channel struct { // ClientContext ... type ClientContext interface { UserContext() fab.User - CryptoSuite() bccsp.BCCSP + SigningManager() fab.SigningManager NewTxnID() (apitxn.TransactionID, error) } diff --git a/pkg/fabric-client/channel/txnproposer.go b/pkg/fabric-client/channel/txnproposer.go index f88e9a4a5b..56371910f4 100644 --- a/pkg/fabric-client/channel/txnproposer.go +++ b/pkg/fabric-client/channel/txnproposer.go @@ -13,13 +13,11 @@ import ( "github.com/golang/protobuf/proto" protos_utils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protos/utils" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" "github.com/hyperledger/fabric-sdk-go/api/apitxn" - fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal/txnproc" ) @@ -137,8 +135,12 @@ func newTransactionProposal(channelID string, request apitxn.ChaincodeInvokeRequ return nil, fmt.Errorf("Error getting user context: %s", err) } - signature, err := fc.SignObjectWithKey(proposalBytes, user.PrivateKey(), - &bccsp.SHAOpts{}, nil, clientContext.CryptoSuite()) + signingMgr := clientContext.SigningManager() + if signingMgr == nil { + return nil, fmt.Errorf("Signing Manager is nil") + } + + signature, err := signingMgr.Sign(proposalBytes, user.PrivateKey()) if err != nil { return nil, err } @@ -185,7 +187,12 @@ func (c *Channel) signProposal(proposal *pb.Proposal) (*pb.SignedProposal, error return nil, fmt.Errorf("Error mashalling proposal: %s", err) } - signature, err := fc.SignObjectWithKey(proposalBytes, user.PrivateKey(), &bccsp.SHAOpts{}, nil, c.clientContext.CryptoSuite()) + signingMgr := c.clientContext.SigningManager() + if signingMgr == nil { + return nil, fmt.Errorf("Signing Manager is nil") + } + + signature, err := signingMgr.Sign(proposalBytes, user.PrivateKey()) if err != nil { return nil, fmt.Errorf("Error signing proposal: %s", err) } diff --git a/pkg/fabric-client/channel/txnsender.go b/pkg/fabric-client/channel/txnsender.go index 1608b8f44b..f8e052dd08 100644 --- a/pkg/fabric-client/channel/txnsender.go +++ b/pkg/fabric-client/channel/txnsender.go @@ -16,10 +16,9 @@ import ( proto_ts "github.com/golang/protobuf/ptypes/timestamp" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" "github.com/hyperledger/fabric-sdk-go/api/apitxn" + protos_utils "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protos/utils" - fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal/txnproc" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" ) @@ -300,8 +299,12 @@ func (c *Channel) SignPayload(payload []byte) (*fab.SignedEnvelope, error) { return nil, fmt.Errorf("User is nil") } - signature, err := fc.SignObjectWithKey(payload, user.PrivateKey(), - &bccsp.SHAOpts{}, nil, c.clientContext.CryptoSuite()) + signingMgr := c.clientContext.SigningManager() + if signingMgr == nil { + return nil, fmt.Errorf("Signing Manager is nil") + } + + signature, err := signingMgr.Sign(payload, user.PrivateKey()) if err != nil { return nil, err } diff --git a/pkg/fabric-client/client.go b/pkg/fabric-client/client.go index f58fa71d16..1e4affcfd0 100644 --- a/pkg/fabric-client/client.go +++ b/pkg/fabric-client/client.go @@ -37,11 +37,12 @@ var logger = logging.MustGetLogger("fabric_sdk_go") // Client enables access to a Fabric network. type Client struct { - channels map[string]fab.Channel - cryptoSuite bccsp.BCCSP - stateStore fab.KeyValueStore - userContext fab.User - config config.Config + channels map[string]fab.Channel + cryptoSuite bccsp.BCCSP + stateStore fab.KeyValueStore + userContext fab.User + config config.Config + signingManager fab.SigningManager } // NewClient returns a Client instance. @@ -114,6 +115,16 @@ func (c *Client) CryptoSuite() bccsp.BCCSP { return c.cryptoSuite } +// SigningManager returns the signing manager +func (c *Client) SigningManager() fab.SigningManager { + return c.signingManager +} + +// SetSigningManager is a convenience method to set signing manager +func (c *Client) SetSigningManager(signingMgr fab.SigningManager) { + c.signingManager = signingMgr +} + // SaveUserToStateStore ... /* * Sets an instance of the User class as the security context of this client instance. This user’s credentials (ECert) will be @@ -268,11 +279,16 @@ func (c *Client) SignChannelConfig(config []byte) (*common.ConfigSignature, erro return nil, fmt.Errorf("User is nil") } + signingMgr := c.SigningManager() + if signingMgr == nil { + return nil, fmt.Errorf("Signing Manager is nil") + } + // get all the bytes to be signed together, then sign signingBytes := fcutils.ConcatenateBytes(signatureHeaderBytes, config) - signature, err := fc.SignObjectWithKey(signingBytes, user.PrivateKey(), &bccsp.SHAOpts{}, nil, c.CryptoSuite()) + signature, err := signingMgr.Sign(signingBytes, user.PrivateKey()) if err != nil { - return nil, fmt.Errorf("error singing config: %v", err) + return nil, fmt.Errorf("error signing config: %v", err) } // build the return object @@ -399,9 +415,14 @@ func (c *Client) createOrUpdateChannel(request fab.CreateChannelRequest, haveEnv return fmt.Errorf("error marshaling payload: %v", err) } - signature, err = fc.SignObjectWithKey(payloadBytes, c.userContext.PrivateKey(), &bccsp.SHAOpts{}, nil, c.CryptoSuite()) + signingMgr := c.SigningManager() + if signingMgr == nil { + return fmt.Errorf("Signing Manager is nil") + } + + signature, err = signingMgr.Sign(payloadBytes, c.UserContext().PrivateKey()) if err != nil { - return fmt.Errorf("error singing payload: %v", err) + return fmt.Errorf("error signing payload: %v", err) } } @@ -505,7 +526,13 @@ func (c *Client) InstallChaincode(chaincodeName string, chaincodePath string, ch if user == nil { return nil, "", fmt.Errorf("User is nil") } - signature, err := fc.SignObjectWithKey(proposalBytes, user.PrivateKey(), &bccsp.SHAOpts{}, nil, c.CryptoSuite()) + + signingMgr := c.SigningManager() + if signingMgr == nil { + return nil, "", fmt.Errorf("Signing Manager is nil") + } + + signature, err := signingMgr.Sign(proposalBytes, user.PrivateKey()) if err != nil { return nil, "", err } diff --git a/pkg/fabric-client/client_test.go b/pkg/fabric-client/client_test.go index 969d8f2fc3..b9aa78033c 100644 --- a/pkg/fabric-client/client_test.go +++ b/pkg/fabric-client/client_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 package fabricclient import ( + "bytes" "fmt" "io/ioutil" "testing" @@ -122,6 +123,19 @@ func TestClientMethods(t *testing.T) { t.Fatalf("client.StateStore().GetValue() didn't return the right value") } + // Set and use siging manager + client.SetSigningManager(mocks.NewMockSigningManager()) + + greeting := []byte("Hello") + signedObj, err := client.SigningManager().Sign(greeting, user.PrivateKey()) + if err != nil { + t.Fatalf("Failed to sign object.") + } + + if !bytes.Equal(signedObj, greeting) { + t.Fatalf("Expecting Hello, got %s", signedObj) + } + } func TestCreateChannel(t *testing.T) { diff --git a/pkg/fabric-client/credentialmgr/credentialmgr.go b/pkg/fabric-client/credentialmgr/credentialmgr.go new file mode 100644 index 0000000000..34d15253ef --- /dev/null +++ b/pkg/fabric-client/credentialmgr/credentialmgr.go @@ -0,0 +1,125 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package credentialmgr + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" + "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + fabricCaUtil "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric-ca/util" + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" +) + +// CredentialManager is used for retriving user's signing identity (ecert + private key) +type CredentialManager struct { + orgName string + keyDir string + certDir string + config apiconfig.Config + cryptoProvider bccsp.BCCSP +} + +// NewCredentialManager Constructor for a credential manager. +// @param {string} orgName - organisation id +// @returns {CredentialManager} new credential manager +func NewCredentialManager(orgName string, config apiconfig.Config, cryptoProvider bccsp.BCCSP) (apifabclient.CredentialManager, error) { + + netConfig, err := config.NetworkConfig() + if err != nil { + return nil, err + } + + // viper keys are case insensitive + orgConfig, ok := netConfig.Organizations[strings.ToLower(orgName)] + if !ok { + return nil, fmt.Errorf("Unable to retrieve org config for %s", orgName) + } + + if orgConfig.CryptoPath == "" { + return nil, fmt.Errorf("Must provide crypto config path") + } + + orgCryptoPath := orgConfig.CryptoPath + if !filepath.IsAbs(orgCryptoPath) { + orgCryptoPath = filepath.Join(config.CryptoConfigPath(), orgCryptoPath) + } + + return &CredentialManager{orgName: orgName, config: config, keyDir: orgCryptoPath + "/keystore", certDir: orgCryptoPath + "/signcerts", cryptoProvider: cryptoProvider}, nil +} + +// GetSigningIdentity will sign the given object with provided key, +func (mgr *CredentialManager) GetSigningIdentity(userName string) (*apifabclient.SigningIdentity, error) { + + if userName == "" { + return nil, fmt.Errorf("Must provide user name") + } + + privateKeyDir := strings.Replace(mgr.keyDir, "{userName}", userName, -1) + enrollmentCertDir := strings.Replace(mgr.certDir, "{userName}", userName, -1) + + privateKeyPath, err := getFirstPathFromDir(privateKeyDir) + if err != nil { + return nil, fmt.Errorf("Error finding the private key path: %v", err) + } + + enrollmentCertPath, err := getFirstPathFromDir(enrollmentCertDir) + if err != nil { + return nil, fmt.Errorf("Error finding the enrollment cert path: %v", err) + } + + mspID, err := mgr.config.MspID(mgr.orgName) + if err != nil { + return nil, fmt.Errorf("Error reading MSP ID config: %s", err) + } + + privateKey, err := fabricCaUtil.ImportBCCSPKeyFromPEM(privateKeyPath, mgr.cryptoProvider, true) + if err != nil { + return nil, fmt.Errorf("Error importing private key: %v", err) + } + enrollmentCert, err := ioutil.ReadFile(enrollmentCertPath) + if err != nil { + return nil, fmt.Errorf("Error reading from the enrollment cert path: %v", err) + } + + signingIdentity := &apifabclient.SigningIdentity{MspID: mspID, PrivateKey: privateKey, EnrollmentCert: enrollmentCert} + + return signingIdentity, nil + +} + +// Gets the first path from the dir directory +func getFirstPathFromDir(dir string) (string, error) { + + files, err := ioutil.ReadDir(dir) + if err != nil { + return "", fmt.Errorf("Could not read directory %s, err %s", err, dir) + } + + for _, p := range files { + if p.IsDir() { + continue + } + + fullName := filepath.Join(dir, string(filepath.Separator), p.Name()) + fmt.Printf("Reading file %s\n", fullName) + } + + for _, f := range files { + if f.IsDir() { + continue + } + + fullName := filepath.Join(dir, string(filepath.Separator), f.Name()) + return fullName, nil + } + + return "", fmt.Errorf("No paths found in directory: %s", dir) +} diff --git a/pkg/fabric-client/credentialmgr/credentialmgr_test.go b/pkg/fabric-client/credentialmgr/credentialmgr_test.go new file mode 100644 index 0000000000..df368c538a --- /dev/null +++ b/pkg/fabric-client/credentialmgr/credentialmgr_test.go @@ -0,0 +1,69 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package credentialmgr + +import ( + "fmt" + "testing" + + "github.com/hyperledger/fabric-sdk-go/pkg/config" + fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" +) + +func TestCredentialManager(t *testing.T) { + + config, err := config.InitConfig("../../../test/fixtures/config/config_test.yaml") + if err != nil { + fmt.Println(err.Error()) + } + + credentialMgr, err := NewCredentialManager("Org1", config, &fcmocks.MockCryptoSuite{}) + if err != nil { + t.Fatalf("Failed to setup credential manager: %s", err) + } + + _, err = credentialMgr.GetSigningIdentity("") + if err == nil { + t.Fatalf("Should have failed to retrieve signing identity for empty user name") + } + + _, err = credentialMgr.GetSigningIdentity("Non-Existent") + if err == nil { + t.Fatalf("Should have failed to retrieve signing identity for non-existent user") + } + + _, err = credentialMgr.GetSigningIdentity("User1") + if err != nil { + t.Fatalf("Failed to retrieve signing identity: %s", err) + } + +} + +func TestInvalidOrgCredentialManager(t *testing.T) { + + config, err := config.InitConfig("../../../test/fixtures/config/config_test.yaml") + if err != nil { + fmt.Println(err.Error()) + } + + // Invalid Org + credentialMgr, err := NewCredentialManager("invalidOrg", config, &fcmocks.MockCryptoSuite{}) + if err == nil { + t.Fatalf("Should have failed to setup manager for invalid org") + } + + // Valid Org, Invalid User + credentialMgr, err = NewCredentialManager("Org1", config, &fcmocks.MockCryptoSuite{}) + if err != nil { + t.Fatalf("Failed to setup credential manager: %s", err) + } + _, err = credentialMgr.GetSigningIdentity("testUser") + if err == nil { + t.Fatalf("Should have failed to retrieve signing identity for invalid user name") + } + +} diff --git a/pkg/fabric-client/events/consumer/consumer.go b/pkg/fabric-client/events/consumer/consumer.go index db9cfa82c1..13b7d75911 100644 --- a/pkg/fabric-client/events/consumer/consumer.go +++ b/pkg/fabric-client/events/consumer/consumer.go @@ -7,23 +7,22 @@ SPDX-License-Identifier: Apache-2.0 package consumer import ( + "context" "fmt" "io" "sync" "time" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "github.com/golang/protobuf/proto" apiconfig "github.com/hyperledger/fabric-sdk-go/api/apiconfig" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" consumer "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/events/consumer" - fc "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/internal" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" ehpb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" logging "github.com/op/go-logging" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" ) var logger = logging.MustGetLogger("fabric_sdk_go") @@ -91,8 +90,13 @@ func (ec *eventsClient) send(emsg *ehpb.Event) error { if err != nil { return fmt.Errorf("Error marshaling message: %s", err) } - signature, err := fc.SignObjectWithKey(payload, user.PrivateKey(), - &bccsp.SHAOpts{}, nil, ec.client.CryptoSuite()) + + signingMgr := ec.client.SigningManager() + if signingMgr == nil { + return fmt.Errorf("Signing Manager is nil") + } + + signature, err := signingMgr.Sign(payload, user.PrivateKey()) if err != nil { return fmt.Errorf("Error signing message: %s", err) } diff --git a/pkg/fabric-client/internal/util.go b/pkg/fabric-client/internal/util.go index 98c3c6ecf7..ef005f2f29 100644 --- a/pkg/fabric-client/internal/util.go +++ b/pkg/fabric-client/internal/util.go @@ -10,7 +10,6 @@ import ( "fmt" ab "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protos/orderer" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/crypto" @@ -94,21 +93,6 @@ func BuildHeader(creator []byte, channelHeader *common.ChannelHeader, nonce []by return header, nil } -// SignObjectWithKey will sign the given object with the given key, -// hashOpts and signerOpts -func SignObjectWithKey(object []byte, key bccsp.Key, - hashOpts bccsp.HashOpts, signerOpts bccsp.SignerOpts, cryptoSuite bccsp.BCCSP) ([]byte, error) { - digest, err := cryptoSuite.Hash(object, hashOpts) - if err != nil { - return nil, err - } - signature, err := cryptoSuite.Sign(key, digest, signerOpts) - if err != nil { - return nil, err - } - return signature, nil -} - // MarshalOrPanic serializes a protobuf message and panics if this operation fails. func MarshalOrPanic(pb proto.Message) []byte { data, err := proto.Marshal(pb) diff --git a/pkg/fabric-client/mocks/mockclient.go b/pkg/fabric-client/mocks/mockclient.go index ea179a8682..d2bda59279 100644 --- a/pkg/fabric-client/mocks/mockclient.go +++ b/pkg/fabric-client/mocks/mockclient.go @@ -20,26 +20,27 @@ import ( // MockClient ... type MockClient struct { - channels map[string]fab.Channel - cryptoSuite bccsp.BCCSP - stateStore fab.KeyValueStore - userContext fab.User - config config.Config - errorScenario bool + channels map[string]fab.Channel + cryptoSuite bccsp.BCCSP + stateStore fab.KeyValueStore + userContext fab.User + config config.Config + errorScenario bool + signingManager fab.SigningManager } // NewMockClient ... /* * Returns a FabricClient instance */ -func NewMockClient() fab.FabricClient { +func NewMockClient() *MockClient { channels := make(map[string]fab.Channel) - c := &MockClient{channels: channels, cryptoSuite: nil, stateStore: nil, userContext: nil, config: NewMockConfig()} + c := &MockClient{channels: channels, cryptoSuite: nil, stateStore: nil, userContext: nil, config: NewMockConfig(), signingManager: NewMockSigningManager()} return c } //NewMockInvalidClient : Returns new Mock FabricClient with error flag on used to test invalid scenarios -func NewMockInvalidClient() fab.FabricClient { +func NewMockInvalidClient() *MockClient { channels := make(map[string]fab.Channel) c := &MockClient{channels: channels, cryptoSuite: nil, stateStore: nil, userContext: nil, config: NewMockConfig(), errorScenario: true} return c @@ -85,6 +86,16 @@ func (c *MockClient) CryptoSuite() bccsp.BCCSP { return c.cryptoSuite } +// SigningManager returns the signing manager +func (c *MockClient) SigningManager() fab.SigningManager { + return c.signingManager +} + +// SetSigningManager mocks setting signing manager +func (c *MockClient) SetSigningManager(signingMgr fab.SigningManager) { + c.signingManager = signingMgr +} + // SaveUserToStateStore ... func (c *MockClient) SaveUserToStateStore(user fab.User, skipPersistence bool) error { return fmt.Errorf("Not implemented yet") diff --git a/pkg/fabric-client/mocks/mockconfig.go b/pkg/fabric-client/mocks/mockconfig.go index 6cc7b1da0d..df5b363226 100644 --- a/pkg/fabric-client/mocks/mockconfig.go +++ b/pkg/fabric-client/mocks/mockconfig.go @@ -163,6 +163,16 @@ func (c *MockConfig) NetworkConfig() (*config.NetworkConfig, error) { return nil, nil } +// ChannelConfig returns the channel configuration +func (c *MockConfig) ChannelConfig(name string) (*config.ChannelConfig, error) { + return nil, nil +} + +// ChannelPeers returns the channel peers configuration +func (c *MockConfig) ChannelPeers(name string) ([]config.ChannelPeer, error) { + return nil, nil +} + // Ephemeral flag func (c *MockConfig) Ephemeral() bool { return false diff --git a/pkg/fabric-client/mocks/mocksigningmgr.go b/pkg/fabric-client/mocks/mocksigningmgr.go new file mode 100644 index 0000000000..fafc2ca1e6 --- /dev/null +++ b/pkg/fabric-client/mocks/mocksigningmgr.go @@ -0,0 +1,30 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package mocks + +import ( + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" + + "github.com/hyperledger/fabric-sdk-go/api/apifabclient" +) + +// MockSigningManager is mock signing manager +type MockSigningManager struct { + cryptoProvider bccsp.BCCSP + hashOpts bccsp.HashOpts + signerOpts bccsp.SignerOpts +} + +// NewMockSigningManager Constructor for a mock signing manager. +func NewMockSigningManager() apifabclient.SigningManager { + return &MockSigningManager{} +} + +// Sign will sign the given object using provided key +func (mgr *MockSigningManager) Sign(object []byte, key bccsp.Key) ([]byte, error) { + return object, nil +} diff --git a/pkg/fabric-client/signingmgr/signingmgr.go b/pkg/fabric-client/signingmgr/signingmgr.go new file mode 100644 index 0000000000..b0848d026b --- /dev/null +++ b/pkg/fabric-client/signingmgr/signingmgr.go @@ -0,0 +1,52 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package signingmgr + +import ( + "fmt" + + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp" + + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" +) + +// SigningManager is used for signing objects with private key +type SigningManager struct { + cryptoProvider bccsp.BCCSP + hashOpts bccsp.HashOpts + signerOpts bccsp.SignerOpts +} + +// NewSigningManager Constructor for a signing manager. +// @param {BCCSP} cryptoProvider - crypto provider +// @param {Config} config - configuration provider +// @returns {SigningManager} new signing manager +func NewSigningManager(cryptoProvider bccsp.BCCSP, config apiconfig.Config) (*SigningManager, error) { + return &SigningManager{cryptoProvider: cryptoProvider, hashOpts: &bccsp.SHAOpts{}}, nil +} + +// Sign will sign the given object using provided key +func (mgr *SigningManager) Sign(object []byte, key bccsp.Key) ([]byte, error) { + + if object == nil || len(object) == 0 { + return nil, fmt.Errorf("Must provide object to sign") + } + + if key == nil { + return nil, fmt.Errorf("Must provide key for signing") + } + + digest, err := mgr.cryptoProvider.Hash(object, mgr.hashOpts) + if err != nil { + return nil, err + } + signature, err := mgr.cryptoProvider.Sign(key, digest, mgr.signerOpts) + if err != nil { + return nil, err + } + return signature, nil +} diff --git a/pkg/fabric-client/signingmgr/signingmgr_test.go b/pkg/fabric-client/signingmgr/signingmgr_test.go new file mode 100644 index 0000000000..e9584268cc --- /dev/null +++ b/pkg/fabric-client/signingmgr/signingmgr_test.go @@ -0,0 +1,49 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package signingmgr + +import ( + "bytes" + "testing" + + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-ca-client/mocks" + fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" +) + +func TestSigningManager(t *testing.T) { + + signingMgr, err := NewSigningManager(&fcmocks.MockCryptoSuite{}, &fcmocks.MockConfig{}) + if err != nil { + t.Fatalf("Failed to setup discovery provider: %s", err) + } + + _, err = signingMgr.Sign(nil, nil) + if err == nil { + t.Fatalf("Should have failed to sign nil object") + } + + _, err = signingMgr.Sign([]byte(""), nil) + if err == nil { + t.Fatalf("Should have failed to sign object empty object") + } + + _, err = signingMgr.Sign([]byte("Hello"), nil) + if err == nil { + t.Fatalf("Should have failed to sign object with nil key") + } + + signedObj, err := signingMgr.Sign([]byte("Hello"), &mocks.MockKey{}) + if err != nil { + t.Fatalf("Failed to sign object: %s", err) + } + + expectedObj := []byte("testSignature") + if !bytes.Equal(signedObj, expectedObj) { + t.Fatalf("Expecting %s, got %s", expectedObj, signedObj) + } + +} diff --git a/pkg/fabric-txn/chclient/chclient.go b/pkg/fabric-txn/chclient/chclient.go new file mode 100644 index 0000000000..5adc95aede --- /dev/null +++ b/pkg/fabric-txn/chclient/chclient.go @@ -0,0 +1,233 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package chclient enables channel client +package chclient + +import ( + "fmt" + "reflect" + "time" + + fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/api/apitxn" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/internal" +) + +const ( + defaultQueryTimeout = time.Second * 20 + defaultExecuteTxTimeout = time.Second * 30 +) + +// ChannelClient enables access to a Fabric network. +type ChannelClient struct { + client fab.FabricClient + channel fab.Channel + discovery fab.DiscoveryService + eventHub fab.EventHub +} + +// NewChannelClient returns a ChannelClient instance. +func NewChannelClient(client fab.FabricClient, channel fab.Channel, discovery fab.DiscoveryService, eventHub fab.EventHub) (*ChannelClient, error) { + + channelClient := ChannelClient{client: client, channel: channel, discovery: discovery, eventHub: eventHub} + + return &channelClient, nil +} + +// Query chaincode +func (cc *ChannelClient) Query(request apitxn.QueryRequest) ([]byte, error) { + + return cc.QueryWithOpts(request, apitxn.QueryOpts{Timeout: defaultQueryTimeout}) + +} + +// QueryWithOpts allows the user to provide options for query (sync vs async, etc.) +func (cc *ChannelClient) QueryWithOpts(request apitxn.QueryRequest, opts apitxn.QueryOpts) ([]byte, error) { + + if request.ChaincodeID == "" || request.Fcn == "" { + return nil, fmt.Errorf("Chaincode name and function name must be provided") + } + + notifier := opts.Notifier + if notifier == nil { + notifier = make(chan apitxn.QueryResponse) + } + + peers, err := cc.discovery.GetPeers(request.ChaincodeID) + if err != nil { + return nil, fmt.Errorf("Unable to get peers: %v", err) + } + + txProcessors := peer.PeersToTxnProcessors(peers) + + go sendTransactionProposal(request, cc.channel, txProcessors, notifier) + + if opts.Notifier != nil { + return nil, nil + } + + timeout := defaultQueryTimeout + if opts.Timeout != 0 { + timeout = opts.Timeout + } + + select { + case response := <-notifier: + return response.Response, response.Error + case <-time.After(timeout): + return nil, fmt.Errorf("Query request timed out") + } + +} + +func sendTransactionProposal(request apitxn.QueryRequest, channel fab.Channel, proposalProcessors []apitxn.ProposalProcessor, notifier chan apitxn.QueryResponse) { + + // TODO: Temporary conversion until proposal sender is changed to handle [][]byte arguments + ccArgs := toStringArray(request.Args) + transactionProposalResponses, _, err := internal.CreateAndSendTransactionProposal(channel, + request.ChaincodeID, request.Fcn, ccArgs, proposalProcessors, nil) + + if err != nil { + notifier <- apitxn.QueryResponse{Response: nil, Error: err} + return + } + + response := transactionProposalResponses[0].ProposalResponse.GetResponse().Payload + + notifier <- apitxn.QueryResponse{Response: response, Error: nil} +} + +// ExecuteTx prepares and executes transaction +func (cc *ChannelClient) ExecuteTx(request apitxn.ExecuteTxRequest) (apitxn.TransactionID, error) { + + return cc.ExecuteTxWithOpts(request, apitxn.ExecuteTxOpts{Timeout: defaultExecuteTxTimeout}) +} + +// ExecuteTxWithOpts allows the user to provide options for execute transaction: +// sync vs async, filter to inspect proposal response before commit etc) +func (cc *ChannelClient) ExecuteTxWithOpts(request apitxn.ExecuteTxRequest, opts apitxn.ExecuteTxOpts) (apitxn.TransactionID, error) { + + if request.ChaincodeID == "" || request.Fcn == "" { + return apitxn.TransactionID{}, fmt.Errorf("Chaincode name and function name must be provided") + } + + peers, err := cc.discovery.GetPeers(request.ChaincodeID) + if err != nil { + return apitxn.TransactionID{}, fmt.Errorf("Unable to get peers: %v", err) + } + + // TODO: Temporary conversion until proposal sender is changed to handle [][]byte arguments + ccArgs := toStringArray(request.Args) + txProposalResponses, txID, err := internal.CreateAndSendTransactionProposal(cc.channel, + request.ChaincodeID, request.Fcn, ccArgs, peer.PeersToTxnProcessors(peers), request.TransientMap) + if err != nil { + return apitxn.TransactionID{}, fmt.Errorf("CreateAndSendTransactionProposal returned error: %v", err) + } + + if opts.TxFilter != nil { + txProposalResponses, err = opts.TxFilter.ProcessTxProposalResponse(txProposalResponses) + if err != nil { + return txID, fmt.Errorf("TxFilter returned error: %v", err) + } + } + + notifier := opts.Notifier + if notifier == nil { + notifier = make(chan apitxn.ExecuteTxResponse) + } + + timeout := defaultExecuteTxTimeout + if opts.Timeout != 0 { + timeout = opts.Timeout + } + + go sendTransaction(cc.channel, txID, txProposalResponses, cc.eventHub, notifier, timeout) + + if opts.Notifier != nil { + return txID, nil + } + + select { + case response := <-notifier: + return response.Response, response.Error + case <-time.After(timeout): // This should never happen since there's timeout in sendTransaction + return txID, fmt.Errorf("ExecuteTx request timed out") + } + +} + +func sendTransaction(channel fab.Channel, txID apitxn.TransactionID, txProposalResponses []*apitxn.TransactionProposalResponse, eventHub fab.EventHub, notifier chan apitxn.ExecuteTxResponse, timeout time.Duration) { + + if eventHub.IsConnected() == false { + err := eventHub.Connect() + if err != nil { + notifier <- apitxn.ExecuteTxResponse{Response: apitxn.TransactionID{}, Error: err} + } + } + + done, fail := internal.RegisterTxEvent(txID, eventHub) + _, err := internal.CreateAndSendTransaction(channel, txProposalResponses) + if err != nil { + notifier <- apitxn.ExecuteTxResponse{Response: apitxn.TransactionID{}, Error: fmt.Errorf("CreateAndSendTransaction returned error: %v", err)} + return + } + + select { + case <-done: + notifier <- apitxn.ExecuteTxResponse{Response: txID, Error: nil} + case err := <-fail: + notifier <- apitxn.ExecuteTxResponse{Response: txID, Error: fmt.Errorf("ExecuteTx received an error from eventhub for txid(%s), error(%v)", txID, err)} + case <-time.After(timeout): + notifier <- apitxn.ExecuteTxResponse{Response: txID, Error: fmt.Errorf("ExecuteTx didn't receive block event for txid(%s)", txID)} + } +} + +// Close releases channel client resources (disconnects event hub etc.) +func (cc *ChannelClient) Close() error { + if cc.eventHub.IsConnected() == true { + return cc.eventHub.Disconnect() + } + + return nil +} + +// RegisterChaincodeEvent registers chain code event +// @param {chan bool} channel which receives event details when the event is complete +// @returns {object} object handle that should be used to unregister +func (cc *ChannelClient) RegisterChaincodeEvent(notify chan<- *apitxn.CCEvent, chainCodeID string, eventID string) apitxn.Registration { + + // Register callback for CE + rce := cc.eventHub.RegisterChaincodeEvent(chainCodeID, eventID, func(ce *fab.ChaincodeEvent) { + notify <- &apitxn.CCEvent{ChaincodeID: ce.ChaincodeID, EventName: ce.EventName, TxID: ce.TxID, Payload: ce.Payload} + }) + + return rce +} + +// UnregisterChaincodeEvent removes chain code event registration +func (cc *ChannelClient) UnregisterChaincodeEvent(registration apitxn.Registration) error { + + switch regType := registration.(type) { + + case *fab.ChainCodeCBE: + cc.eventHub.UnregisterChaincodeEvent(regType) + default: + return fmt.Errorf("Unsupported registration type: %v", reflect.TypeOf(registration)) + } + + return nil + +} + +func toStringArray(byteArray [][]byte) []string { + strArray := make([]string, len(byteArray)) + for i := 0; i < len(byteArray); i++ { + strArray[i] = string(byteArray[i]) + } + return strArray +} diff --git a/pkg/fabric-txn/chclient/chclient_test.go b/pkg/fabric-txn/chclient/chclient_test.go new file mode 100644 index 0000000000..3c3bb6aa8d --- /dev/null +++ b/pkg/fabric-txn/chclient/chclient_test.go @@ -0,0 +1,213 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package chclient + +import ( + "fmt" + "testing" + "time" + + "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + + "github.com/hyperledger/fabric-sdk-go/api/apitxn" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel" + fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" + txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/mocks" +) + +func TestQuery(t *testing.T) { + + chClient := setupChannelClient(nil, t) + + result, err := chClient.Query(apitxn.QueryRequest{}) + if err == nil { + t.Fatalf("Should have failed for empty query request") + } + + result, err = chClient.Query(apitxn.QueryRequest{Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}) + if err == nil { + t.Fatalf("Should have failed for empty chaincode ID") + } + + result, err = chClient.Query(apitxn.QueryRequest{ChaincodeID: "testCC", Args: [][]byte{[]byte("query"), []byte("b")}}) + if err == nil { + t.Fatalf("Should have failed for empty function") + } + + result, err = chClient.Query(apitxn.QueryRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}) + if err != nil { + t.Fatalf("Failed to invoke test cc: %s", err) + } + + if result != nil { + t.Fatalf("Expecting nil, got %s", result) + } + +} + +func TestQueryDiscoveryError(t *testing.T) { + + chClient := setupChannelClient(fmt.Errorf("Test Error"), t) + + _, err := chClient.Query(apitxn.QueryRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}) + if err == nil { + t.Fatalf("Should have failed to query with error in discovery.GetPeers()") + } + +} + +func TestQueryWithOptSync(t *testing.T) { + + chClient := setupChannelClient(nil, t) + + result, err := chClient.QueryWithOpts(apitxn.QueryRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}, apitxn.QueryOpts{}) + if err != nil { + t.Fatalf("Failed to invoke test cc: %s", err) + } + + if result != nil { + t.Fatalf("Expecting nil, got %s", result) + } +} + +func TestQueryWithOptAsync(t *testing.T) { + + chClient := setupChannelClient(nil, t) + + notifier := make(chan apitxn.QueryResponse) + + result, err := chClient.QueryWithOpts(apitxn.QueryRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}, apitxn.QueryOpts{Notifier: notifier}) + if err != nil { + t.Fatalf("Failed to invoke test cc: %s", err) + } + + if result != nil { + t.Fatalf("Expecting nil, got %s", result) + } + + select { + case response := <-notifier: + if response.Error != nil { + t.Fatalf("Query returned error: %s", response.Error) + } + if string(response.Response) != "" { + t.Fatalf("Expecting empty, got %s", response.Response) + } + case <-time.After(time.Second * 20): + t.Fatalf("Query Request timed out") + } + +} + +func TestQueryWithOptTarget(t *testing.T) { + + chClient := setupChannelClient(nil, t) + + testPeer := fcmocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil} + + peers := []apifabclient.Peer{&testPeer} + + targets := peer.PeersToTxnProcessors(peers) + + result, err := chClient.QueryWithOpts(apitxn.QueryRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}, apitxn.QueryOpts{ProposalProcessors: targets}) + if err != nil { + t.Fatalf("Failed to invoke test cc: %s", err) + } + + if result != nil { + t.Fatalf("Expecting nil, got %s", result) + } +} + +func TestExecuteTx(t *testing.T) { + + chClient := setupChannelClient(nil, t) + + _, err := chClient.ExecuteTx(apitxn.ExecuteTxRequest{}) + if err == nil { + t.Fatalf("Should have failed for empty invoke request") + } + + _, err = chClient.ExecuteTx(apitxn.ExecuteTxRequest{Fcn: "invoke", Args: [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}}) + if err == nil { + t.Fatalf("Should have failed for empty chaincode ID") + } + + _, err = chClient.ExecuteTx(apitxn.ExecuteTxRequest{ChaincodeID: "testCC", Args: [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}}) + if err == nil { + t.Fatalf("Should have failed for empty function") + } + + // TODO: Test Valid Scenario with mocks +} + +func TestExecuteTxDiscoveryError(t *testing.T) { + + chClient := setupChannelClient(fmt.Errorf("Test Error"), t) + + _, err := chClient.ExecuteTx(apitxn.ExecuteTxRequest{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}}) + if err == nil { + t.Fatalf("Should have failed to execute tx with error in discovery.GetPeers()") + } + +} + +func setupTestChannel() (*channel.Channel, error) { + client := setupTestClient() + return channel.NewChannel("testChannel", client) +} + +func setupTestClient() *fcmocks.MockClient { + client := fcmocks.NewMockClient() + user := fcmocks.NewMockUser("test") + cryptoSuite := &fcmocks.MockCryptoSuite{} + client.SaveUserToStateStore(user, true) + client.SetUserContext(user) + client.SetCryptoSuite(cryptoSuite) + return client +} + +func setupTestDiscovery(discErr error, peers []apifabclient.Peer) (apifabclient.DiscoveryService, error) { + + testChannel, err := setupTestChannel() + if err != nil { + return nil, fmt.Errorf("Failed to setup test channel: %s", err) + } + + mockDiscovery, err := txnmocks.NewMockDiscoveryProvider(discErr, peers) + if err != nil { + return nil, fmt.Errorf("Failed to setup discovery provider: %s", err) + } + + return mockDiscovery.NewDiscoveryService(testChannel) +} + +func setupChannelClient(discErr error, t *testing.T) *ChannelClient { + + fcClient := setupTestClient() + + testChannel, err := setupTestChannel() + if err != nil { + t.Fatalf("Failed to setup test channel: %s", err) + } + + orderer := fcmocks.NewMockOrderer("", nil) + testChannel.AddOrderer(orderer) + + discoveryService, err := setupTestDiscovery(discErr, nil) + if err != nil { + t.Fatalf("Failed to setup discovery service: %s", err) + } + + ch, err := NewChannelClient(fcClient, testChannel, discoveryService, nil) + if err != nil { + t.Fatalf("Failed to create new channel client: %s", err) + } + + return ch +} diff --git a/pkg/fabric-txn/discovery/staticdiscovery.go b/pkg/fabric-txn/discovery/staticdiscovery.go new file mode 100644 index 0000000000..246d3c8526 --- /dev/null +++ b/pkg/fabric-txn/discovery/staticdiscovery.go @@ -0,0 +1,68 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package discovery + +import ( + "fmt" + + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" + "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer" +) + +/** + * Discovery Provider is used to discover peers on the network + */ + +// StaticDiscoveryProvider implements discovery provider +type StaticDiscoveryProvider struct { + config apiconfig.Config +} + +// StaticDiscoveryService implements discovery service +type StaticDiscoveryService struct { + config apiconfig.Config + channel apifabclient.Channel + peers []apifabclient.Peer +} + +// NewDiscoveryProvider returns discovery provider +func NewDiscoveryProvider(config apiconfig.Config) (*StaticDiscoveryProvider, error) { + return &StaticDiscoveryProvider{config: config}, nil +} + +// NewDiscoveryService return discovery service for specific channel +func (dp *StaticDiscoveryProvider) NewDiscoveryService(channel apifabclient.Channel) (apifabclient.DiscoveryService, error) { + + peerConfig, err := dp.config.ChannelPeers(channel.Name()) + if err != nil { + return nil, fmt.Errorf("Unable to read configuration for channel(%s) peers: %s", channel.Name(), err) + } + + peers := []apifabclient.Peer{} + + for _, p := range peerConfig { + + serverHostOverride := "" + if str, ok := p.GrpcOptions["ssl-target-name-override"].(string); ok { + serverHostOverride = str + } + peer, err := peer.NewPeerTLSFromCert(p.Url, p.TlsCACerts.Path, serverHostOverride, dp.config) + if err != nil { + return nil, fmt.Errorf("NewPeer return error: %v", err) + } + peers = append(peers, peer) + } + + return &StaticDiscoveryService{channel: channel, config: dp.config, peers: peers}, nil +} + +// GetPeers is used to discover eligible peers for chaincode +func (ds *StaticDiscoveryService) GetPeers(chaincodeID string) ([]apifabclient.Peer, error) { + // TODO: Incorporate CC policy here + return ds.peers, nil +} diff --git a/pkg/fabric-txn/discovery/staticdiscovery_test.go b/pkg/fabric-txn/discovery/staticdiscovery_test.go new file mode 100644 index 0000000000..f0d9d9d538 --- /dev/null +++ b/pkg/fabric-txn/discovery/staticdiscovery_test.go @@ -0,0 +1,75 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package discovery + +import ( + "fmt" + "testing" + + "github.com/hyperledger/fabric-sdk-go/pkg/config" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel" + fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" +) + +func TestDiscovery(t *testing.T) { + + invalidChannel, err := setupChannel("not-configured") + if err != nil { + t.Fatalf("Failed to setup channel: %s", err) + } + + validChannel, err := setupChannel("mychannel") + if err != nil { + t.Fatalf("Failed to setup channel: %s", err) + } + + config, err := config.InitConfig("../../../test/fixtures/config/config_test.yaml") + if err != nil { + fmt.Println(err.Error()) + } + + discoveryProvider, err := NewDiscoveryProvider(config) + if err != nil { + t.Fatalf("Failed to setup discovery provider: %s", err) + } + + discoveryService, err := discoveryProvider.NewDiscoveryService(invalidChannel) + if err == nil { + t.Fatalf("Should have failed to setup discovery service for non-configured channel") + } + + discoveryService, err = discoveryProvider.NewDiscoveryService(validChannel) + if err != nil { + t.Fatalf("Failed to setup discovery service: %s", err) + } + + peers, err := discoveryService.GetPeers("testCC") + if err != nil { + t.Fatalf("Failed to get peers from discovery service: %s", err) + } + + expectedNumOfPeeers := 1 + if len(peers) != expectedNumOfPeeers { + t.Fatalf("Expecting %d, got %d peers", expectedNumOfPeeers, len(peers)) + } + +} + +func setupChannel(name string) (*channel.Channel, error) { + client := setupTestClient() + return channel.NewChannel(name, client) +} + +func setupTestClient() *fcmocks.MockClient { + client := fcmocks.NewMockClient() + user := fcmocks.NewMockUser("test") + cryptoSuite := &fcmocks.MockCryptoSuite{} + client.SaveUserToStateStore(user, true) + client.SetUserContext(user) + client.SetCryptoSuite(cryptoSuite) + return client +} diff --git a/pkg/fabric-txn/mocks/mockdiscovery.go b/pkg/fabric-txn/mocks/mockdiscovery.go new file mode 100644 index 0000000000..9a1e67a28a --- /dev/null +++ b/pkg/fabric-txn/mocks/mockdiscovery.go @@ -0,0 +1,56 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package mocks + +import ( + "github.com/hyperledger/fabric-sdk-go/api/apifabclient" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks" +) + +/** + * Mock Discovery Provider is used to mock peers on the network + */ + +// MockStaticDiscoveryProvider implements mock discovery provider +type MockStaticDiscoveryProvider struct { + Error error + Peers []apifabclient.Peer +} + +// MockStaticDiscoveryService implements mock discovery service +type MockStaticDiscoveryService struct { + Error error + Peers []apifabclient.Peer +} + +// NewMockDiscoveryProvider returns mock discovery provider +func NewMockDiscoveryProvider(err error, peers []apifabclient.Peer) (*MockStaticDiscoveryProvider, error) { + return &MockStaticDiscoveryProvider{Error: err, Peers: peers}, nil +} + +// NewDiscoveryService return discovery service for specific channel +func (dp *MockStaticDiscoveryProvider) NewDiscoveryService(channel apifabclient.Channel) (apifabclient.DiscoveryService, error) { + return &MockStaticDiscoveryService{Error: dp.Error, Peers: dp.Peers}, nil +} + +// GetPeers is used to discover eligible peers for chaincode +func (ds *MockStaticDiscoveryService) GetPeers(chaincodeID string) ([]apifabclient.Peer, error) { + + if ds.Error != nil { + return nil, ds.Error + } + + if ds.Peers == nil { + mockPeer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil} + peers := make([]apifabclient.Peer, 0) + peers = append(peers, &mockPeer) + ds.Peers = peers + } + + return ds.Peers, nil + +} diff --git a/test/fixtures/config/config_pkcs11_test.yaml b/test/fixtures/config/config_pkcs11_test.yaml index 2001d6245f..433a024f8f 100755 --- a/test/fixtures/config/config_pkcs11_test.yaml +++ b/test/fixtures/config/config_pkcs11_test.yaml @@ -126,11 +126,6 @@ channels: # Default: true eventSource: true - peer0.org2.example.com: - endorsingPeer: true - chaincodeQuery: true - ledgerQuery: true - eventSource: true # [Optional]. what chaincodes are expected to exist on this channel? The application can use # this information to validate that the target peers are in the expected state by comparing @@ -147,6 +142,9 @@ organizations: Org1: mspid: Org1MSP + # Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode) + cryptoPath: peerOrganizations/org1.example.com/users/{userName}@org1.example.com/msp + peers: - peer0.org1.example.com @@ -177,12 +175,24 @@ organizations: # fabric-ca registrar enroll ID and secret, etc. Org2: mspid: Org2MSP + + # Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode) + cryptoPath: peerOrganizations/org2.example.com/users/{userName}@org2.example.com/msp + peers: - peer0.org2.example.com certificateAuthorities: - ca-org2 + # Orderer Org name + ordererorg: + # Membership Service Provider ID for this organization + mspID: "OrdererOrg" + + # 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 diff --git a/test/fixtures/config/config_test.yaml b/test/fixtures/config/config_test.yaml index a6ab1a5466..b4bbb27827 100755 --- a/test/fixtures/config/config_test.yaml +++ b/test/fixtures/config/config_test.yaml @@ -35,9 +35,10 @@ 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: org1 + organization: Org1 logging: level: info @@ -123,11 +124,7 @@ channels: # Default: true eventSource: true - peer0.org2.example.com: - endorsingPeer: true - chaincodeQuery: true - ledgerQuery: true - eventSource: true + # [Optional]. what chaincodes are expected to exist on this channel? The application can use # this information to validate that the target peers are in the expected state by comparing @@ -141,9 +138,12 @@ channels: # list of participating organizations in this network # organizations: - org1: + Org1: mspid: Org1MSP + # Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode) + cryptoPath: peerOrganizations/org1.example.com/users/{userName}@org1.example.com/msp + peers: - peer0.org1.example.com @@ -172,14 +172,27 @@ organizations: # 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: + Org2: mspid: Org2MSP + + # Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode) + cryptoPath: peerOrganizations/org2.example.com/users/{userName}@org2.example.com/msp + peers: - peer0.org2.example.com certificateAuthorities: - ca-org2 + # Orderer Org name + ordererorg: + # Membership Service Provider ID for this organization + mspID: "OrdererOrg" + + # 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 @@ -274,4 +287,4 @@ certificateAuthorities: enrollId: admin enrollSecret: adminpw # [Optional] The optional name of the CA. - caName: ca-org2 \ No newline at end of file + caName: ca-org2 diff --git a/test/integration/base_test_setup.go b/test/integration/base_test_setup.go index de6e306a71..744a440068 100644 --- a/test/integration/base_test_setup.go +++ b/test/integration/base_test_setup.go @@ -40,7 +40,6 @@ type BaseSetupImpl struct { Initialized bool ChannelConfig string AdminUser ca.User - NormalUser ca.User } // Initialize reads configuration from file and sets up client, channel and event hub @@ -59,43 +58,18 @@ func (setup *BaseSetupImpl) Initialize() error { return fmt.Errorf("Error initializing SDK: %s", err) } - context, err := sdk.NewContext(setup.OrgID) + session, err := sdk.NewPreEnrolledUserSession(setup.OrgID, "Admin") if err != nil { - return fmt.Errorf("Error getting a context for org: %s", err) + return fmt.Errorf("Error getting admin user session for org: %s", err) } - user, err := deffab.NewUser(sdk.ConfigProvider(), context.MSPClient(), "admin", "adminpw", setup.OrgID) - if err != nil { - return fmt.Errorf("NewUser returned error: %v", err) - } - - session1, err := sdk.NewSession(context, user) - if err != nil { - return fmt.Errorf("NewSession returned error: %v", err) - } - sc, err := sdk.NewSystemClient(session1) + sc, err := sdk.NewSystemClient(session) if err != nil { return fmt.Errorf("NewSystemClient returned error: %v", err) } - err = sc.SaveUserToStateStore(user, false) - if err != nil { - return fmt.Errorf("client.SaveUserToStateStore returned error: %v", err) - } setup.Client = sc - - org1Admin, err := GetAdmin(sc, "org1", setup.OrgID) - if err != nil { - return fmt.Errorf("Error getting org admin user: %v", err) - } - - org1User, err := GetUser(sc, "org1", setup.OrgID) - if err != nil { - return fmt.Errorf("Error getting org user: %v", err) - } - - setup.AdminUser = org1Admin - setup.NormalUser = org1User + setup.AdminUser = session.Identity() channel, err := setup.GetChannel(setup.Client, setup.ChannelID, []string{setup.OrgID}) if err != nil { @@ -103,37 +77,33 @@ func (setup *BaseSetupImpl) Initialize() error { } setup.Channel = channel - ordererAdmin, err := GetOrdererAdmin(sc, setup.OrgID) + ordererAdmin, err := sdk.NewPreEnrolledUser("ordererorg", "Admin") if err != nil { return fmt.Errorf("Error getting orderer admin user: %v", err) } // Check if primary peer has joined channel - alreadyJoined, err := HasPrimaryPeerJoinedChannel(sc, org1Admin, channel) + alreadyJoined, err := HasPrimaryPeerJoinedChannel(sc, channel) if err != nil { return fmt.Errorf("Error while checking if primary peer has already joined channel: %v", err) } if !alreadyJoined { // Create, initialize and join channel - if err = admin.CreateOrUpdateChannel(sc, ordererAdmin, org1Admin, channel, setup.ChannelConfig); err != nil { + if err = admin.CreateOrUpdateChannel(sc, ordererAdmin, setup.AdminUser, channel, setup.ChannelConfig); err != nil { return fmt.Errorf("CreateChannel returned error: %v", err) } time.Sleep(time.Second * 3) - sc.SetUserContext(org1Admin) if err = channel.Initialize(nil); err != nil { return fmt.Errorf("Error initializing channel: %v", err) } - if err = admin.JoinChannel(sc, org1Admin, channel); err != nil { + if err = admin.JoinChannel(sc, setup.AdminUser, channel); err != nil { return fmt.Errorf("JoinChannel returned error: %v", err) } } - //by default client's user context should use regular user, for admin actions, UserContext must be set to AdminUser - sc.SetUserContext(org1User) - if err := setup.setupEventHub(sc); err != nil { return err } @@ -170,11 +140,6 @@ func (setup *BaseSetupImpl) InitConfig() (apiconfig.Config, error) { // InstantiateCC ... func (setup *BaseSetupImpl) InstantiateCC(chainCodeID string, chainCodePath string, chainCodeVersion string, args []string) error { - // InstantiateCC requires AdminUser privileges so setting user context with Admin User - setup.Client.SetUserContext(setup.AdminUser) - - // must reset client user context to normal user once done with Admin privilieges - defer setup.Client.SetUserContext(setup.NormalUser) chaincodePolicy := cauthdsl.SignedByMspMember(setup.Client.UserContext().MspID()) @@ -183,11 +148,6 @@ func (setup *BaseSetupImpl) InstantiateCC(chainCodeID string, chainCodePath stri // UpgradeCC ... func (setup *BaseSetupImpl) UpgradeCC(chainCodeID string, chainCodePath string, chainCodeVersion string, args []string) error { - // InstantiateCC requires AdminUser privileges so setting user context with Admin User - setup.Client.SetUserContext(setup.AdminUser) - - // must reset client user context to normal user once done with Admin privilieges - defer setup.Client.SetUserContext(setup.NormalUser) chaincodePolicy := cauthdsl.SignedByMspMember(setup.Client.UserContext().MspID()) @@ -196,11 +156,6 @@ func (setup *BaseSetupImpl) UpgradeCC(chainCodeID string, chainCodePath string, // InstallCC ... func (setup *BaseSetupImpl) InstallCC(chainCodeID string, chainCodePath string, chainCodeVersion string, chaincodePackage []byte) error { - // installCC requires AdminUser privileges so setting user context with Admin User - setup.Client.SetUserContext(setup.AdminUser) - - // must reset client user context to normal user once done with Admin privilieges - defer setup.Client.SetUserContext(setup.NormalUser) if err := admin.SendInstallCC(setup.Client, chainCodeID, chainCodePath, chainCodeVersion, chaincodePackage, setup.Channel.Peers(), setup.GetDeployPath()); err != nil { return fmt.Errorf("SendInstallProposal return error: %v", err) diff --git a/test/integration/channel_client_test.go b/test/integration/channel_client_test.go new file mode 100644 index 0000000000..12072e873f --- /dev/null +++ b/test/integration/channel_client_test.go @@ -0,0 +1,242 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package integration + +import ( + "fmt" + "testing" + "time" + + "github.com/hyperledger/fabric-sdk-go/api/apitxn" + "github.com/hyperledger/fabric-sdk-go/def/fabapi" + "github.com/hyperledger/fabric-sdk-go/def/fabapi/opt" +) + +var queryArgs = [][]byte{[]byte("query"), []byte("b")} +var txArgs = [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")} + +func TestChannelClient(t *testing.T) { + + testSetup := BaseSetupImpl{ + ConfigFile: ConfigTestFile, + ChannelID: "mychannel", + OrgID: org1Name, + ChannelConfig: "../fixtures/channel/mychannel.tx", + ConnectEventHub: true, + } + + if err := testSetup.Initialize(); err != nil { + t.Fatalf(err.Error()) + } + + if err := testSetup.InstallAndInstantiateExampleCC(); err != nil { + t.Fatalf("InstallAndInstantiateExampleCC return error: %v", err) + } + + // Create SDK setup for the integration tests + sdkOptions := fabapi.Options{ + ConfigFile: testSetup.ConfigFile, + StateStoreOpts: opt.StateStoreOpts{ + Path: "/tmp/enroll_user", + }, + } + + sdk, err := fabapi.NewSDK(sdkOptions) + if err != nil { + t.Fatalf("Failed to create new SDK: %s", err) + } + + chClient, err := sdk.NewChannelClient(testSetup.ChannelID, "User1") + if err != nil { + t.Fatalf("Failed to create new channel client: %s", err) + } + + // Synchronous query + testQuery("200", testSetup.ChainCodeID, chClient, t) + + // Synchronous transaction + _, err = chClient.ExecuteTx(apitxn.ExecuteTxRequest{ChaincodeID: testSetup.ChainCodeID, Fcn: "invoke", Args: txArgs}) + if err != nil { + t.Fatalf("Failed to move funds: %s", err) + } + + // Verify transaction using asynchronous query + testQueryWithOpts("201", testSetup.ChainCodeID, chClient, t) + + // Asynchronous transaction + testAsyncTransaction(testSetup.ChainCodeID, chClient, t) + + // Verify asynchronous transaction + testQuery("202", testSetup.ChainCodeID, chClient, t) + + // Test transaction filter error + testFilterError(testSetup.ChainCodeID, chClient, t) + + // Test commit error + testCommitError(testSetup.ChainCodeID, chClient, t) + + // Verify that filter error and commit error did not modify value + testQuery("202", testSetup.ChainCodeID, chClient, t) + + // Test register and receive chaincode event + testChaincodeEvent(testSetup.ChainCodeID, chClient, t) + + // Verify transaction with chain code event completed + testQuery("203", testSetup.ChainCodeID, chClient, t) + + // Release channel client resources + err = chClient.Close() + if err != nil { + t.Fatalf("Failed to close channel client: %v", err) + } + +} + +func testQuery(expected string, ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + result, err := chClient.Query(apitxn.QueryRequest{ChaincodeID: ccID, Fcn: "invoke", Args: queryArgs}) + if err != nil { + t.Fatalf("Failed to invoke example cc: %s", err) + } + + if string(result) != expected { + t.Fatalf("Expecting %s, got %s", expected, result) + } +} + +func testQueryWithOpts(expected string, ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + notifier := make(chan apitxn.QueryResponse) + result, err := chClient.QueryWithOpts(apitxn.QueryRequest{ChaincodeID: ccID, Fcn: "invoke", Args: queryArgs}, apitxn.QueryOpts{Notifier: notifier}) + if err != nil { + t.Fatalf("Failed to invoke example cc asynchronously: %s", err) + } + if result != nil { + t.Fatalf("Expecting empty, got %s", result) + } + + select { + case response := <-notifier: + if response.Error != nil { + t.Fatalf("Query returned error: %s", response.Error) + } + if string(response.Response) != expected { + t.Fatalf("Expecting %s, got %s", expected, response.Response) + } + case <-time.After(time.Second * 20): + t.Fatalf("Query Request timed out") + } + +} + +func testAsyncTransaction(ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + txNotifier := make(chan apitxn.ExecuteTxResponse) + txFilter := &TestTxFilter{} + txOpts := apitxn.ExecuteTxOpts{Notifier: txNotifier, TxFilter: txFilter} + + _, err := chClient.ExecuteTxWithOpts(apitxn.ExecuteTxRequest{ChaincodeID: ccID, Fcn: "invoke", Args: txArgs}, txOpts) + if err != nil { + t.Fatalf("Failed to move funds: %s", err) + } + + select { + case response := <-txNotifier: + if response.Error != nil { + t.Fatalf("ExecuteTx returned error: %s", response.Error) + } + case <-time.After(time.Second * 20): + t.Fatalf("ExecuteTx timed out") + } +} + +func testCommitError(ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + txNotifier := make(chan apitxn.ExecuteTxResponse) + + txFilter := &TestTxFilter{errResponses: fmt.Errorf("Error")} + txOpts := apitxn.ExecuteTxOpts{Notifier: txNotifier, TxFilter: txFilter} + + _, err := chClient.ExecuteTxWithOpts(apitxn.ExecuteTxRequest{ChaincodeID: ccID, Fcn: "invoke", Args: txArgs}, txOpts) + if err != nil { + t.Fatalf("Failed to move funds: %s", err) + } + + select { + case response := <-txNotifier: + if response.Error == nil { + t.Fatalf("ExecuteTx should have returned an error") + } + case <-time.After(time.Second * 20): + t.Fatalf("ExecuteTx timed out") + } +} + +func testFilterError(ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + txFilter := &TestTxFilter{err: fmt.Errorf("Error")} + txOpts := apitxn.ExecuteTxOpts{TxFilter: txFilter} + + _, err := chClient.ExecuteTxWithOpts(apitxn.ExecuteTxRequest{ChaincodeID: ccID, Fcn: "invoke", Args: txArgs}, txOpts) + if err == nil { + t.Fatalf("Should have failed with filter error") + } + +} + +type TestTxFilter struct { + err error + errResponses error +} + +func (tf *TestTxFilter) ProcessTxProposalResponse(txProposalResponse []*apitxn.TransactionProposalResponse) ([]*apitxn.TransactionProposalResponse, error) { + if tf.err != nil { + return nil, tf.err + } + + var newResponses []*apitxn.TransactionProposalResponse + + if tf.errResponses != nil { + // 404 will cause transaction commit error + txProposalResponse[0].ProposalResponse.Response.Status = 404 + } + + newResponses = append(newResponses, txProposalResponse[0]) + return newResponses, nil +} + +func testChaincodeEvent(ccID string, chClient apitxn.ChannelClient, t *testing.T) { + + eventID := "test([a-zA-Z]+)" + + // Register chaincode event (pass in channel which receives event details when the event is complete) + notifier := make(chan *apitxn.CCEvent) + rce := chClient.RegisterChaincodeEvent(notifier, ccID, eventID) + + // Synchronous transaction + txID, err := chClient.ExecuteTx(apitxn.ExecuteTxRequest{ChaincodeID: ccID, Fcn: "invoke", Args: txArgs}) + if err != nil { + t.Fatalf("Failed to move funds: %s", err) + } + + select { + case ccEvent := <-notifier: + fmt.Printf("Received cc event: %s\n", ccEvent) + if ccEvent.TxID != txID.ID { + t.Fatalf("CCEvent(%s) and ExecuteTx(%s) transaction IDs don't match", ccEvent.TxID, txID.ID) + } + case <-time.After(time.Second * 20): + t.Fatalf("Did NOT receive CC for eventId(%s)\n", eventID) + } + + // Unregister chain code event using registration handle + err = chClient.UnregisterChaincodeEvent(rce) + if err != nil { + t.Fatalf("Unregister cc event failed: %s", err) + } + +} diff --git a/test/integration/channel_queries_test.go b/test/integration/channel_queries_test.go index 63d6f2ee2e..c65798a3b5 100644 --- a/test/integration/channel_queries_test.go +++ b/test/integration/channel_queries_test.go @@ -189,10 +189,6 @@ func testInstalledChaincodes(t *testing.T, channel fab.Channel, client fab.Fabri // Our target will be primary peer on this channel target := channel.PrimaryPeer() fmt.Printf("****QueryInstalledChaincodes for %s\n", target.URL()) - // Test Query Installed chaincodes for target (primary) - // set Client User Context to Admin first - testSetup.Client.SetUserContext(testSetup.AdminUser) - defer testSetup.Client.SetUserContext(testSetup.NormalUser) chaincodeQueryResponse, err := client.QueryInstalledChaincodes(target) if err != nil { @@ -295,7 +291,6 @@ func testQueryByChaincode(t *testing.T, channel fab.Channel, config config.Confi t.Fatalf("QueryByChaincode number of results mismatch. Expected: %d Got: %d", len(targets), len(queryResponses)) } - testSetup.Client.SetUserContext(testSetup.NormalUser) channel.RemovePeer(firstInvalidTarget) channel.RemovePeer(secondInvalidTarget) } diff --git a/test/integration/fabric_ca_test.go b/test/integration/fabric_ca_test.go index 35593ab733..f7e24dbc8c 100644 --- a/test/integration/fabric_ca_test.go +++ b/test/integration/fabric_ca_test.go @@ -26,8 +26,8 @@ import ( fabricCAClient "github.com/hyperledger/fabric-sdk-go/pkg/fabric-ca-client" ) -var org1Name = "org1" -var org2Name = "org2" +var org1Name = "Org1" +var org2Name = "Org2" // This test loads/enrols an admin user // Using the admin, it registers, enrols, and revokes a test user diff --git a/test/integration/install_chaincode_test.go b/test/integration/install_chaincode_test.go index 1178112397..7d059de8b5 100644 --- a/test/integration/install_chaincode_test.go +++ b/test/integration/install_chaincode_test.go @@ -56,9 +56,6 @@ func testChaincodeInstallUsingChaincodePath(t *testing.T, testSetup *BaseSetupIm t.Fatalf("installCC return error: %v", err) } - // set Client User Context to Admin - testSetup.Client.SetUserContext(testSetup.AdminUser) - defer testSetup.Client.SetUserContext(testSetup.NormalUser) chaincodeQueryResponse, err := client.QueryInstalledChaincodes(testSetup.Channel.PrimaryPeer()) if err != nil { t.Fatalf("QueryInstalledChaincodes return error: %v", err) diff --git a/test/integration/main_test.go b/test/integration/main_test.go index 110c57dfa0..33b24cf74d 100644 --- a/test/integration/main_test.go +++ b/test/integration/main_test.go @@ -27,7 +27,7 @@ func setup() { var err error testSetup := BaseSetupImpl{ - ConfigFile: "../fixtures/config/config_test.yaml", + ConfigFile: ConfigTestFile, } testFabricConfig, err = testSetup.InitConfig() diff --git a/test/integration/orgs/test_setup.go b/test/integration/orgs/test_setup.go index a836e2de5f..5ce8055858 100644 --- a/test/integration/orgs/test_setup.go +++ b/test/integration/orgs/test_setup.go @@ -7,12 +7,18 @@ SPDX-License-Identifier: Apache-2.0 package orgs import ( + "fmt" "testing" "time" + "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/signingmgr" + ca "github.com/hyperledger/fabric-sdk-go/api/apifabca" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" "github.com/hyperledger/fabric-sdk-go/api/apitxn" + + deffab "github.com/hyperledger/fabric-sdk-go/def/fabapi" + "github.com/hyperledger/fabric-sdk-go/def/fabapi/opt" "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric-sdk-go/pkg/config" client "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client" @@ -25,8 +31,8 @@ import ( "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" ) -var org1 = "org1" -var org2 = "org2" +var org1 = "Org1" +var org2 = "Org2" // Client var orgTestClient fab.FabricClient @@ -64,7 +70,7 @@ func initializeFabricClient(t *testing.T) { } // Instantiate client - orgTestClient = client.NewClient(configImpl) + fcClient := client.NewClient(configImpl) // Initialize crypto suite err = factory.InitFactories(configImpl.CSPConfig()) @@ -72,7 +78,17 @@ func initializeFabricClient(t *testing.T) { t.Fatal(err) } cryptoSuite := factory.GetDefault() - orgTestClient.SetCryptoSuite(cryptoSuite) + fcClient.SetCryptoSuite(cryptoSuite) + + signingMgr, err := signingmgr.NewSigningManager(cryptoSuite, configImpl) + if err != nil { + t.Fatal(err) + } + + fcClient.SetSigningManager(signingMgr) + + // From now on use interface only + orgTestClient = fcClient } func createTestChannel(t *testing.T) { @@ -89,7 +105,9 @@ func createTestChannel(t *testing.T) { orgTestChannel.AddOrderer(orgTestOrderer) - foundChannel, err = integration.HasPrimaryPeerJoinedChannel(orgTestClient, org1User, orgTestChannel) + orgTestClient.SetUserContext(org1User) + + foundChannel, err = integration.HasPrimaryPeerJoinedChannel(orgTestClient, orgTestChannel) if err != nil { t.Fatal(err) } @@ -228,26 +246,38 @@ func loadOrgPeers(t *testing.T) { func loadOrgUsers(t *testing.T) { var err error - ordererAdminUser, err = integration.GetOrdererAdmin(orgTestClient, org1) - if err != nil { - t.Fatal(err) - } - org1AdminUser, err = integration.GetAdmin(orgTestClient, "org1", org1) - if err != nil { - t.Fatal(err) + // Create SDK setup for the integration tests + sdkOptions := deffab.Options{ + ConfigFile: "../" + integration.ConfigTestFile, + StateStoreOpts: opt.StateStoreOpts{ + Path: "/tmp/enroll_user", + }, } - org2AdminUser, err = integration.GetAdmin(orgTestClient, "org2", org2) - if err != nil { - t.Fatal(err) - } - org1User, err = integration.GetUser(orgTestClient, "org1", org1) + + sdk, err := deffab.NewSDK(sdkOptions) if err != nil { t.Fatal(err) } - org2User, err = integration.GetUser(orgTestClient, "org2", org2) + + ordererAdminUser = loadOrgUser(t, sdk, "ordererorg", "Admin") + + org1AdminUser = loadOrgUser(t, sdk, org1, "Admin") + org2AdminUser = loadOrgUser(t, sdk, org2, "Admin") + + org1User = loadOrgUser(t, sdk, org1, "User1") + org2User = loadOrgUser(t, sdk, org2, "User1") + +} + +func loadOrgUser(t *testing.T, sdk *deffab.FabricSDK, orgName string, userName string) fab.User { + + user, err := sdk.NewPreEnrolledUser(orgName, userName) if err != nil { - t.Fatal(err) + t.Fatal(fmt.Errorf("Error getting pre-enrolled user(%s,%s): %v", orgName, userName, err)) } + + return user + } func generateInitArgs() []string { diff --git a/test/integration/utils.go b/test/integration/utils.go index 47e3ce0a77..d2285f0e3c 100644 --- a/test/integration/utils.go +++ b/test/integration/utils.go @@ -8,39 +8,12 @@ package integration import ( "fmt" - "io/ioutil" "math/rand" - "path/filepath" "time" - ca "github.com/hyperledger/fabric-sdk-go/api/apifabca" fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient" - deffab "github.com/hyperledger/fabric-sdk-go/def/fabapi" ) -// GetOrdererAdmin returns a pre-enrolled orderer admin user -func GetOrdererAdmin(c fab.FabricClient, orgName string) (ca.User, error) { - keyDir := "ordererOrganizations/example.com/users/Admin@example.com/msp/keystore" - certDir := "ordererOrganizations/example.com/users/Admin@example.com/msp/signcerts" - return getDefaultImplPreEnrolledUser(c, keyDir, certDir, "ordererAdmin", orgName) -} - -// GetAdmin returns a pre-enrolled org admin user -func GetAdmin(c fab.FabricClient, orgPath string, orgName string) (ca.User, error) { - keyDir := fmt.Sprintf("peerOrganizations/%s.example.com/users/Admin@%s.example.com/msp/keystore", orgPath, orgPath) - certDir := fmt.Sprintf("peerOrganizations/%s.example.com/users/Admin@%s.example.com/msp/signcerts", orgPath, orgPath) - username := fmt.Sprintf("peer%sAdmin", orgPath) - return getDefaultImplPreEnrolledUser(c, keyDir, certDir, username, orgName) -} - -// GetUser returns a pre-enrolled org user -func GetUser(c fab.FabricClient, orgPath string, orgName string) (ca.User, error) { - keyDir := fmt.Sprintf("peerOrganizations/%s.example.com/users/User1@%s.example.com/msp/keystore", orgPath, orgPath) - certDir := fmt.Sprintf("peerOrganizations/%s.example.com/users/User1@%s.example.com/msp/signcerts", orgPath, orgPath) - username := fmt.Sprintf("peer%sUser1", orgPath) - return getDefaultImplPreEnrolledUser(c, keyDir, certDir, username, orgName) -} - // GenerateRandomID generates random ID func GenerateRandomID() string { rand.Seed(time.Now().UnixNano()) @@ -57,66 +30,12 @@ func randomString(strlen int) string { return string(result) } -// GetDefaultImplPreEnrolledUser ... -func getDefaultImplPreEnrolledUser(client fab.FabricClient, keyDir string, certDir string, username string, orgName string) (ca.User, error) { - privateKeyDir := filepath.Join(client.Config().CryptoConfigPath(), keyDir) - privateKeyPath, err := getFirstPathFromDir(privateKeyDir) - if err != nil { - return nil, fmt.Errorf("Error finding the private key path: %v", err) - } - - enrollmentCertDir := filepath.Join(client.Config().CryptoConfigPath(), certDir) - enrollmentCertPath, err := getFirstPathFromDir(enrollmentCertDir) - if err != nil { - return nil, fmt.Errorf("Error finding the enrollment cert path: %v", err) - } - mspID, err := client.Config().MspID(orgName) - if err != nil { - return nil, fmt.Errorf("Error reading MSP ID config: %s", err) - } - return deffab.NewPreEnrolledUser(client.Config(), privateKeyPath, enrollmentCertPath, username, mspID, client.CryptoSuite()) -} - -// Gets the first path from the dir directory -func getFirstPathFromDir(dir string) (string, error) { - - files, err := ioutil.ReadDir(dir) - if err != nil { - return "", fmt.Errorf("Could not read directory %s, err %s", err, dir) - } - - for _, p := range files { - if p.IsDir() { - continue - } - - fullName := filepath.Join(dir, string(filepath.Separator), p.Name()) - fmt.Printf("Reading file %s\n", fullName) - } - - for _, f := range files { - if f.IsDir() { - continue - } - - fullName := filepath.Join(dir, string(filepath.Separator), f.Name()) - return fullName, nil - } - - return "", fmt.Errorf("No paths found in directory: %s", dir) -} - // HasPrimaryPeerJoinedChannel checks whether the primary peer of a channel // has already joined the channel. It returns true if it has, false otherwise, // or an error -func HasPrimaryPeerJoinedChannel(client fab.FabricClient, orgUser ca.User, channel fab.Channel) (bool, error) { +func HasPrimaryPeerJoinedChannel(client fab.FabricClient, channel fab.Channel) (bool, error) { foundChannel := false primaryPeer := channel.PrimaryPeer() - - currentUser := client.UserContext() - defer client.SetUserContext(currentUser) - - client.SetUserContext(orgUser) response, err := client.QueryChannels(primaryPeer) if err != nil { return false, fmt.Errorf("Error querying channel for primary peer: %s", err)