Skip to content

Commit

Permalink
[FAB-5215] SDK entry point (part 1)
Browse files Browse the repository at this point in the history
This patch adds a basic SDK creation package.
In part 1, we create the basic default implementations and
provide initial SDK configuration/options.

Change-Id: Ia839c3602be257d2ed979f0573a0b38d0e8cbc06
Signed-off-by: Troy Ronda <troy@troyronda.com>
  • Loading branch information
troyronda committed Jul 11, 2017
1 parent af9c616 commit c2c0c70
Show file tree
Hide file tree
Showing 6 changed files with 388 additions and 191 deletions.
228 changes: 65 additions & 163 deletions def/fabapi/fabapi.go
Expand Up @@ -4,197 +4,99 @@ Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

// Package fabapi enables client usage of a Hyperledger Fabric network
package fabapi

import (
"fmt"
"io/ioutil"

fabricCaUtil "github.com/hyperledger/fabric-ca/util"
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"
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"
eventsImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/events"
identityImpl "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/identity"
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"
bccsp "github.com/hyperledger/fabric/bccsp"
bccspFactory "github.com/hyperledger/fabric/bccsp/factory"

"github.com/hyperledger/fabric/bccsp"

"github.com/hyperledger/fabric-sdk-go/api/apiconfig"
"github.com/hyperledger/fabric-sdk-go/api/apifabca"
"github.com/hyperledger/fabric-sdk-go/api/apifabclient"
)

// NewClient returns a new default implementation of the Client interface using the config provided.
// It will save the provided user if requested into the state store.
func NewClient(user fabca.User, skipUserPersistence bool, stateStorePath string, config config.Config) (fab.FabricClient, error) {
client := clientImpl.NewClient(config)
// Options encapsulates configuration for the SDK
type Options struct {
// Quick access options
ConfigFile string
OrgID string

cryptoSuite := bccspFactory.GetDefault()
// Options for default providers
ConfigOpts ConfigOpts
StateStoreOpts StateStoreOpts

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)
}
client.SaveUserToStateStore(user, skipUserPersistence)
// Implementations of client functionality (defaults are used if not specified)
ConfigManager apiconfig.Config
SystemClient apifabclient.FabricClient
MSPClient apifabca.FabricCAClient
StateStore apifabclient.KeyValueStore
CryptoSuite bccsp.BCCSP // TODO - maybe copy this interface into the API package

return client, nil
// TODO extract hard-coded logger
}

// NewClientWithUser returns a new default implementation of the Client interface.
// It creates a default implementation of User, enrolls the user, and saves it to the state store.
func NewClientWithUser(name string, pwd string, orgName string,
stateStorePath string, config config.Config, msp fabca.FabricCAClient) (fab.FabricClient, error) {
client := clientImpl.NewClient(config)

cryptoSuite := bccspFactory.GetDefault()
// FabricSDK provides access to clients being managed by the SDK
type FabricSDK struct {
Options
}

client.SetCryptoSuite(cryptoSuite)
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)
// NewSDK initializes default clients
func NewSDK(options Options) (*FabricSDK, error) {
// Construct SDK opts from the quick access options in setup
sdkOpts := SDKOpts{
ConfigFile: options.ConfigFile,
}

user, err := NewUser(client.Config(), msp, name, pwd, mspID)
if err != nil {
return nil, fmt.Errorf("NewUser returned error: %v", err)
}
err = client.SaveUserToStateStore(user, false)
if err != nil {
return nil, fmt.Errorf("client.SaveUserToStateStore returned error: %v", err)
sdk := FabricSDK{
Options: options,
}

client.SetUserContext(user)

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) (fab.FabricClient, error) {

client := clientImpl.NewClient(config)

cryptoSuite := bccspFactory.GetDefault()

client.SetCryptoSuite(cryptoSuite)
if stateStorePath != "" {
stateStore, err := kvs.CreateNewFileKeyValueStore(stateStorePath)
// Initialize default config provider
if sdk.ConfigManager == nil {
config, err := NewDefaultConfig(sdk.ConfigOpts, sdkOpts)
if err != nil {
return nil, fmt.Errorf("CreateNewFileKeyValueStore returned error[%s]", err)
return nil, fmt.Errorf("Failed to initialize default config [%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) {

key, cert, err := msp.Enroll(name, pwd)
if err != nil {
return nil, fmt.Errorf("Enroll returned error: %v", err)
}
user := identityImpl.NewUser(name, mspID)
user.SetPrivateKey(key)
user.SetEnrollmentCertificate(cert)

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)
sdk.ConfigManager = config
}
enrollmentCert, err := ioutil.ReadFile(enrollmentCertPath)
if err != nil {
return nil, fmt.Errorf("Error reading from the enrollment cert path: %v", err)
}

user := identityImpl.NewUser(username, mspID)
user.SetEnrollmentCertificate(enrollmentCert)
user.SetPrivateKey(privateKey)

return user, nil
}

// NewChannel returns a new default implementation of Channel
func NewChannel(client fab.FabricClient, orderer fab.Orderer, peers []fab.Peer, channelID string) (fab.Channel, error) {

channel, err := client.NewChannel(channelID)
if err != nil {
return nil, fmt.Errorf("NewChannel returned error: %v", err)
// Initialize default crypto provider
if sdk.CryptoSuite == nil {
cryptosuite, err := NewCryptoSuite(sdk.ConfigManager.CSPConfig())
if err != nil {
return nil, fmt.Errorf("Failed to initialize default crypto suite [%s]", err)
}
sdk.CryptoSuite = cryptosuite
}

err = channel.AddOrderer(orderer)
if err != nil {
return nil, fmt.Errorf("Error adding orderer: %v", err)
// Initialize default state store
if sdk.StateStore == nil {
store, err := NewDefaultStateStore(sdk.StateStoreOpts, sdk.ConfigManager)
if err != nil {
return nil, fmt.Errorf("Failed to initialize default state store [%s]", err)
}
sdk.StateStore = store
}

for _, p := range peers {
err = channel.AddPeer(p)
// Initialize default MSP client
if sdk.MSPClient == nil { // TODO: Becomes MSP Manager
client, err := NewCAClient(sdk.OrgID, sdk.ConfigManager)
if err != nil {
return nil, fmt.Errorf("Error adding peer: %v", err)
return nil, fmt.Errorf("Failed to initialize default client [%s]", err)
}
sdk.MSPClient = client
}

return channel, nil
}

// NewEventHub returns a new default implementation of Event Hub
func NewEventHub(client fab.FabricClient) (fab.EventHub, error) {
return eventsImpl.NewEventHub(client)
}

// NewOrderer returns a new default implementation of Orderer
func NewOrderer(url string, certificate string, serverHostOverride string, config config.Config) (fab.Orderer, error) {
return ordererImpl.NewOrderer(url, certificate, serverHostOverride, config)
}

// NewPeer returns a new default implementation of Peer
func NewPeer(url string, certificate string, serverHostOverride string, config config.Config) (fab.Peer, error) {
return peerImpl.NewPeerTLSFromCert(url, certificate, serverHostOverride, config)
}

// NewConfig returns a new default implementation of the Config interface
func NewConfig(configFile string) (config.Config, error) {
return configImpl.InitConfig(configFile)
}

// NewCAClient returns a new default implmentation of the MSP client
func NewCAClient(config config.Config, orgName string) (fabca.FabricCAClient, error) {
mspClient, err := fabricCAClient.NewFabricCAClient(config, orgName)
if err != nil {
return nil, fmt.Errorf("NewFabricCAClient returned error: %v", err)
// Initialize default system client
if sdk.SystemClient == nil { // TODO: May need better name
client := NewSystemClient(sdk.ConfigManager)
sdk.SystemClient = client
}

return mspClient, nil
sdk.SystemClient.SetCryptoSuite(sdk.CryptoSuite)
sdk.SystemClient.SetStateStore(sdk.StateStore)

return &sdk, nil
}
25 changes: 25 additions & 0 deletions def/fabapi/fabapi_test.go
@@ -0,0 +1,25 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package fabapi

import "testing"

func TestNewDefaultSDK(t *testing.T) {

setup := Options{
ConfigFile: "../../test/fixtures/config/config_test.yaml",
OrgID: "org1",
StateStoreOpts: StateStoreOpts{
Path: "/tmp/state",
},
}

_, err := NewSDK(setup)
if err != nil {
t.Fatalf("Error initializing SDK: %s", err)
}
}
36 changes: 36 additions & 0 deletions def/fabapi/optfactory.go
@@ -0,0 +1,36 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package fabapi

import (
"github.com/hyperledger/fabric-sdk-go/api/apiconfig"
fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"
)

// SDKOpts provides bootstrap setup
type SDKOpts struct {
ConfigFile string
}

// ConfigOpts provides setup parameters for Config
type ConfigOpts struct { // TODO (moved ConfigFile to SDKOpts to make setup easier for API consumer)
}

// StateStoreOpts provides setup parameters for KeyValueStore
type StateStoreOpts struct {
Path string
}

// NewDefaultConfig creates a Config using the SDK's default implementation
func NewDefaultConfig(o ConfigOpts, a SDKOpts) (apiconfig.Config, error) {
return NewConfigManager(a.ConfigFile)
}

// NewDefaultStateStore creates a KeyValueStore using the SDK's default implementation
func NewDefaultStateStore(o StateStoreOpts, config apiconfig.Config) (fab.KeyValueStore, error) {
return NewKVStore(o.Path) // TODO: config should have this capability
}

0 comments on commit c2c0c70

Please sign in to comment.