Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 9 additions & 32 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Moreover, each FSC node connects to one or more `trusted` Fabric peers to learn
(transactions committed, events, discovery, and so on...).
Here is a pictorial representation of these networks.

![img.png](imgs/networks.png)
![networks.png](imgs/networks.png)

Then, a network node running FSC is a `Business Party` at the Application/Business Layer.
Business Parties run Views to orchestrate a Business Process and achieve a Business Goal.
Expand All @@ -108,13 +108,13 @@ A business process is started by an `initiator view`.
The Fabric Smart Client consists of `platforms` or `SDKs`. Each platform exposes a coherent set of API to address specific tasks
FSC comes equipped with two default SDKs :

![img.png](imgs/stack.png)
![stack.png](imgs/stack.png)

- The `View` SDK is the core of the FSC. It offers API and services to allow FSC nodes:
- The [`View SDK`](./view-sdk.md) is the core of the FSC. It offers API and services to allow FSC nodes:
- To connect to each other in a peer to peer fashion.
- To manage and execute business views.
- To handle node identities.
- The `Fabric` SDK builds on top of the `View` platform and offers API and services to allow FSC nodes to communicate
- The [`Fabric SDK'](./fabric-sdk.md) builds on top of the `View` platform and offers API and services to allow FSC nodes to communicate
with Fabric. The Fabric module is not just the usual Fabric Client SDK, it is more.
Indeed, we can identify the following components that make the Fabric module different from the current Fabric Client SDKs:
- `Chaincode API`: These are APIs that allow the developer to invoke any chaincode and assemble Fabric transactions
Expand All @@ -130,31 +130,7 @@ FSC comes equipped with two default SDKs :
the node is interested about or helped to assemble. An FSC node does not need to store the entire ledger but
only what is relevant. This storage space allows the FSC nodes to keep temporary version of the transactions
they are assembling before they get submitted for ordering.

### The View SDK

This is the `View SDK` stack:

![img.png](imgs/view-sdk.png)

It consists of the following layers:
- `Services` (light-blue boxes): Services offer the core building blocks to enable interaction between `FSC nodes` and maintain state throughout the execution of a Business Process.
- `View API`: This API offers a useful abstraction to implement Business Processes as interactive protocols in an implementation and blockchain independent way.
- `Driver API`: This API takes the burden of translating calls of the View API into API calls that are implementation-specific.
- `Driver Implementations`: This is the lowest level of the View SDK. A driver implementation is responsible to define and realize the executing and the data of a specified business logic. We provide a `Generic View Driver` that implements the primitives used by the `View API`, such as identities, networking, and ledger-specific details.

### The Fabric SDK

This is the `Fabric SDK` stack:

![img.png](imgs/fabric-sdk.png)

It consists of the following layers:
- `Services` (light-blue boxes): Services offer access to Fabric-specific functionality, such as transaction endorsement, state-based endorsement, and the execution of Chaincode.
- `Fabric API`: This API follows the same abstraction paradigm as the `View API` but provides Fabric-specific functionality to enable `FSC nodes` to communicate with Fabric. Even though this API is specific for Fabric, it allows to abstract away certain details when dealing with a specific Fabric version.
- `Driver API`: This API translates the `Fabric API` to a concrete driver implementation.
- `Driver Implementations`: The Fabric SDK comes with a driver implementation for Fabric V2+.


## Transaction Lifecycle or How to orchestrate a business process

There are two ways to orchestrate transactions using the Fabric Smart Client:
Expand All @@ -181,7 +157,7 @@ and then submit a Fabric transaction.

Here is the pictorial representation of the business process Alice and Bob execute.

![img_3.png](imgs/chaincode_based_example.png)
![chaincode_based_example.png](imgs/chaincode_based_example.png)

Step by step:
- Alice initiates the business process by preparing a draft of the input `D` with her leg.
Expand All @@ -206,7 +182,7 @@ Each approver can run its own business logic to inspect the transaction and then

Here is the pictorial representation of the business process Alice and Bob execute.

![img_4.png](imgs/approvers_based_example.png)
![approvers_based_example.png](imgs/approvers_based_example.png)

Step by step:
- Alice initiates the business process by preparing a transaction `Tx` with her leg.
Expand All @@ -216,4 +192,5 @@ Step by step:
- If `Tx’` gets approved, Alice sends it directly to the Fabric Ordering Service.
- Both parties wait for Fabric to confirm the transaction.

Notice that without the Approvers’ signatures, Fabric rejects the transaction. Indeed, the Approvers are Business Parties that play the role of `Fabric Endorsers`.
Notice that without the Approvers’ signatures, Fabric rejects the transaction.
Indeed, the Approvers are Business Parties that play the role of `Fabric Endorsers`.
12 changes: 12 additions & 0 deletions docs/fabric-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# The Fabric SDK

This is the `Fabric SDK` stack:

![img.png](imgs/fabric-sdk.png)

It consists of the following layers:
- `Services` (light-blue boxes): Services offer access to Fabric-specific functionality, such as transaction endorsement, state-based endorsement, and the execution of Chaincode.
- `Fabric API`: This API follows the same abstraction paradigm as the `View API` but provides Fabric-specific functionality to enable `FSC nodes` to communicate with Fabric. Even though this API is specific for Fabric, it allows to abstract away certain details when dealing with a specific Fabric version.
- `Driver API`: This API translates the `Fabric API` to a concrete driver implementation.
- `Driver Implementations`: The Fabric SDK comes with a driver implementation for Fabric V2+.

11 changes: 11 additions & 0 deletions docs/view-sdk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# The View SDK

This is the `View SDK` stack:

![view-sdk.png](imgs/view-sdk.png)

It consists of the following layers:
- `Services` (light-blue boxes): Services offer the core building blocks to enable interaction between `FSC nodes` and maintain state throughout the execution of a Business Process.
- `View API`: This API offers a useful abstraction to implement Business Processes as interactive protocols in an implementation and blockchain independent way.
- `Driver API`: This API takes the burden of translating calls of the View API into API calls that are implementation-specific.
- `Driver Implementations`: This is the lowest level of the View SDK. A driver implementation is responsible to define and realize the executing and the data of a specified business logic. We provide a `Generic View Driver` that implements the primitives used by the `View API`, such as identities, networking, and ledger-specific details.
6 changes: 4 additions & 2 deletions integration/fsc/pingpong/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,12 @@ package pingpong

import "github.com/hyperledger-labs/fabric-smart-client/platform/view/view"

// InitiatorViewFactory is the factory of Initiator views
type InitiatorViewFactory struct{}

// NewView returns a new instance of the Initiator view
func (i *InitiatorViewFactory) NewView(in []byte) (view.View, error) {
return &Initiator{}, nil
return &Initiator{}, nil
}
```
To answer the second question, we need a way to tell the FSC node which view to execute
Expand Down Expand Up @@ -371,7 +373,7 @@ Let us describe what is happening in the above BDD test:

## Deeper Dive

There are still questions to answers. Here are some:
There are still questions to answer. Here are some:
- How do I configure an FSC node?
- How does an FSC node know where are the other nodes and who they are (their PKs)?
- Where are information stored?
Expand Down
2 changes: 2 additions & 0 deletions integration/fsc/pingpong/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package pingpong

import "github.com/hyperledger-labs/fabric-smart-client/platform/view/view"

// InitiatorViewFactory is the factory of Initiator views
type InitiatorViewFactory struct{}

// NewView returns a new instance of the Initiator view
func (i *InitiatorViewFactory) NewView(in []byte) (view.View, error) {
return &Initiator{}, nil
}
2 changes: 2 additions & 0 deletions integration/nwo/fsc/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func (n *Node) SetBootstrap() *Node {
return n
}

// SetExecutable sets the executable path of this node
func (n *Node) SetExecutable(ExecutablePath string) *Node {
n.ExecutablePath = ExecutablePath

Expand Down Expand Up @@ -163,6 +164,7 @@ func (n *Node) RegisterViewFactory(id string, factory Factory) *Node {
return n
}

// RegisterResponder registers the passed responder to the passed initiator
func (n *Node) RegisterResponder(responder view.View, initiator view.View) *Node {
isResponderPtr := reflect.ValueOf(responder).Kind() == reflect.Ptr
isInitiatorPtr := reflect.ValueOf(initiator).Kind() == reflect.Ptr
Expand Down
3 changes: 2 additions & 1 deletion integration/nwo/fsc/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Topology struct {
Logging *Logging `yaml:"logging,omitempty"`
}

// NewTopology returns an empty FSC network topology.
func NewTopology() *Topology {
return &Topology{
TopologyName: TopologyName,
Expand Down Expand Up @@ -51,7 +52,7 @@ func (t *Topology) AddNodeByTemplate(name string, template *node.Node) *node.Nod
return t.addNode(n)
}

// AddNodeByName adds a new node with the passed name
// AddNodeByName adds an empty new node with the passed name
func (t *Topology) AddNodeByName(name string) *node.Node {
n := node.NewNode(name)
return t.addNode(n)
Expand Down
2 changes: 1 addition & 1 deletion platform/fabric/core/generic/msp/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type BinderService interface {
}

type ConfigProvider interface {
api3.ConfigProvider
api3.ConfigService
}

type DeserializerManager interface {
Expand Down
16 changes: 14 additions & 2 deletions platform/view/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,60 @@ import (
"time"
)

// ConfigService models a configuration registry
type ConfigService struct {
cp driver.ConfigProvider
cp driver.ConfigService
}

// GetString returns the value associated with the key as a string
func (c *ConfigService) GetString(key string) string {
return c.cp.GetString(key)
}

// GetDuration returns the value associated with the key as a duration
func (c *ConfigService) GetDuration(key string) time.Duration {
return c.cp.GetDuration(key)
}

// GetBool returns the value associated with the key asa boolean
func (c *ConfigService) GetBool(key string) bool {
return c.cp.GetBool(key)
}

// GetStringSlice returns the value associated with the key as a slice of strings
func (c *ConfigService) GetStringSlice(key string) []string {
return c.cp.GetStringSlice(key)
}

// IsSet checks to see if the key has been set in any of the data locations
func (c *ConfigService) IsSet(key string) bool {
return c.cp.IsSet(key)
}

// UnmarshalKey takes a single key and unmarshals it into a Struct
func (c *ConfigService) UnmarshalKey(key string, rawVal interface{}) error {
return c.cp.UnmarshalKey(key, rawVal)
}

// ConfigFileUsed returns the file used to populate the config registry
func (c *ConfigService) ConfigFileUsed() string {
return c.cp.ConfigFileUsed()
}

// GetPath allows configuration strings that specify a (config-file) relative path
func (c *ConfigService) GetPath(key string) string {
return c.cp.GetPath(key)
}

// TranslatePath translates the passed path relative to the config path
func (c *ConfigService) TranslatePath(path string) string {
return c.cp.TranslatePath(path)
}

// GetConfigService returns an instance of the config service.
// It panics, if no instance is found.
func GetConfigService(sp ServiceProvider) *ConfigService {
return &ConfigService{
cp: driver.GetConfigProvider(sp),
cp: driver.GetConfigService(sp),
}
}
12 changes: 7 additions & 5 deletions platform/view/driver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (

type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error)

// ConfigProvider models a configuration registry
type ConfigProvider interface {
// ConfigService models a configuration registry
type ConfigService interface {
// GetString returns the value associated with the key as a string
GetString(key string) string
// GetDuration returns the value associated with the key as a duration
Expand All @@ -34,10 +34,12 @@ type ConfigProvider interface {
TranslatePath(path string) string
}

func GetConfigProvider(sp ServiceProvider) ConfigProvider {
s, err := sp.GetService(reflect.TypeOf((*ConfigProvider)(nil)))
// GetConfigService returns an instance of the config service.
// It panics, if no instance is found.
func GetConfigService(sp ServiceProvider) ConfigService {
s, err := sp.GetService(reflect.TypeOf((*ConfigService)(nil)))
if err != nil {
panic(err)
}
return s.(ConfigProvider)
return s.(ConfigService)
}
2 changes: 2 additions & 0 deletions platform/view/driver/endpointservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type EndpointService interface {
AddPKIResolver(pkiResolver PKIResolver) error
}

// GetEndpointService returns an instance of the endpoint service.
// It panics, if no instance is found.
func GetEndpointService(ctx ServiceProvider) EndpointService {
s, err := ctx.GetService(reflect.TypeOf((*EndpointService)(nil)))
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions platform/view/driver/flowmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,22 @@ import (
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
)

// ViewManager manages the lifecycle of views and contexts
type ViewManager interface {
// NewView returns a new instance of the view identified by the passed id and on input
NewView(id string, in []byte) (view.View, error)
// Context returns the context associated to the passed id, an error if not context is found.
Context(contextID string) (view.Context, error)
// InitiateView invokes the passed view and returns the result produced by that view
InitiateView(view view.View) (interface{}, error)
// InitiateContext initiates a new context for the passed view
InitiateContext(view view.View) (view.Context, error)
// TODO: remove this method
Start(ctx context.Context)
}

// GetViewManager returns an instance of the view manager.
// It panics, if no instance is found.
func GetViewManager(sp ServiceProvider) ViewManager {
s, err := sp.GetService(reflect.TypeOf((*ViewManager)(nil)))
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions platform/view/driver/flowregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

// Registry keeps track of the available view and view factories
type Registry interface {
// GetIdentifier returns the identifier of the passed view
GetIdentifier(f view.View) string

// RegisterFactory binds an id to a View Factory
Expand Down
2 changes: 2 additions & 0 deletions platform/view/driver/identityprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type IdentityProvider interface {
Admins() []view.Identity
}

// GetIdentityProvider returns an instance of the IdentityProvider interface.
// It panics, if no instance is found.
func GetIdentityProvider(sp ServiceProvider) IdentityProvider {
s, err := sp.GetService(reflect.TypeOf((*IdentityProvider)(nil)))
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions platform/view/driver/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

//go:generate counterfeiter -o mock/session.go -fake-name Session . Session

// Session encapsulates a communication channel to an endpoint
type Session interface {
view.Session
}
8 changes: 7 additions & 1 deletion platform/view/driver/sigservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ import (
)

type Identity interface {
// Serialize returns the byte representation of this identity
Serialize() ([]byte, error)

// Verify verifies the signature over the passed message.
Verify(message []byte, signature []byte) error
}

type SigningIdentity interface {
Identity

Sign(raw []byte) ([]byte, error)
// Sign signs message bytes and returns the signature or an error on failure.
Sign(message []byte) ([]byte, error)

GetPublicVersion() Identity
}
Expand Down Expand Up @@ -59,9 +62,12 @@ func GetSigService(sp ServiceProvider) SigService {
return s.(SigService)
}

// AuditRegistry models a repository of identities' audit information
type AuditRegistry interface {
// RegisterAuditInfo binds the passed audit info to the passed identity
RegisterAuditInfo(identity view.Identity, info []byte) error

// GetAuditInfo returns the audit info associated to the passed identity, nil if not found
GetAuditInfo(identity view.Identity) ([]byte, error)
}

Expand Down
2 changes: 2 additions & 0 deletions platform/view/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ func (e *EndpointService) AddPKIResolver(pkiResolver PKIResolver) error {
return e.es.AddPKIResolver(pkiResolver)
}

// GetEndpointService returns an instance of the endpoint service.
// It panics, if no instance is found.
func GetEndpointService(sp ServiceProvider) *EndpointService {
return &EndpointService{es: driver.GetEndpointService(sp)}
}
3 changes: 2 additions & 1 deletion platform/view/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func (i *IdentityProvider) Identity(label string) view.Identity {
return i.ip.Identity(label)
}

// GetIdentityProvider returns an instance of the identity provider
// GetIdentityProvider returns an instance of the identity provider.
// It panics, if no instance is found.
func GetIdentityProvider(sp ServiceProvider) *IdentityProvider {
return &IdentityProvider{ip: driver.GetIdentityProvider(sp)}
}
Loading