Skip to content

Commit d61a585

Browse files
committed
[FAB-9723] Infrastructure for pluggable endorsement
This change set adds support in the handlers for pluggable endorsement plugins. Change-Id: Id738e131b3444b2f56f5ac0af1d9f113fbd0cfe5 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent abe1a6f commit d61a585

File tree

6 files changed

+260
-15
lines changed

6 files changed

+260
-15
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package builtin
8+
9+
import (
10+
"github.com/hyperledger/fabric/core/handlers/endorsement/api"
11+
endorsement2 "github.com/hyperledger/fabric/core/handlers/endorsement/api/identities"
12+
"github.com/hyperledger/fabric/protos/peer"
13+
"github.com/pkg/errors"
14+
)
15+
16+
type DefaultESCCFactory struct {
17+
}
18+
19+
func (*DefaultESCCFactory) New() endorsement.Plugin {
20+
return &DefaultESCC{}
21+
}
22+
23+
type DefaultESCC struct {
24+
endorsement2.SigningIdentityFetcher
25+
}
26+
27+
func (e *DefaultESCC) Endorse(prpBytes []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) {
28+
signer, err := e.SigningIdentityForRequest(sp)
29+
if err != nil {
30+
return nil, nil, errors.Wrap(err, "failed fetching signing identity")
31+
}
32+
// serialize the signing identity
33+
identityBytes, err := signer.Serialize()
34+
if err != nil {
35+
return nil, nil, errors.Wrapf(err, "could not serialize the signing identity")
36+
}
37+
38+
// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
39+
signature, err := signer.Sign(append(prpBytes, identityBytes...))
40+
if err != nil {
41+
return nil, nil, errors.Wrapf(err, "could not sign the proposal response payload")
42+
}
43+
endorsement := &peer.Endorsement{Signature: signature, Endorser: identityBytes}
44+
return endorsement, prpBytes, nil
45+
}
46+
47+
func (e *DefaultESCC) Init(dependencies ...endorsement.Dependency) error {
48+
for _, dep := range dependencies {
49+
sIDFetcher, isSigningIdentityFetcher := dep.(endorsement2.SigningIdentityFetcher)
50+
if !isSigningIdentityFetcher {
51+
continue
52+
}
53+
e.SigningIdentityFetcher = sIDFetcher
54+
return nil
55+
}
56+
return errors.New("could not find SigningIdentityFetcher in dependencies")
57+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package main
8+
9+
import (
10+
"errors"
11+
"fmt"
12+
13+
endorsement2 "github.com/hyperledger/fabric/core/handlers/endorsement/api"
14+
endorsement3 "github.com/hyperledger/fabric/core/handlers/endorsement/api/identities"
15+
"github.com/hyperledger/fabric/protos/peer"
16+
)
17+
18+
// To build the plugin,
19+
// run:
20+
// go build -buildmode=plugin -o escc.so escc_plugin.go
21+
22+
// DefaultESCCFactory returns an endorsement plugin factory which returns plugins
23+
// that behave as the default endorsement system chaincode
24+
type DefaultESCCFactory struct {
25+
}
26+
27+
// New returns an endorsement plugin that behaves as the default endorsement system chaincode
28+
func (*DefaultESCCFactory) New() endorsement2.Plugin {
29+
return &DefaultESCC{}
30+
}
31+
32+
// DefaultESCC is an endorsement plugin that behaves as the default endorsement system chaincode
33+
type DefaultESCC struct {
34+
endorsement3.SigningIdentityFetcher
35+
}
36+
37+
// Endorse signs the given payload(ProposalResponsePayload bytes), and optionally mutates it.
38+
// Returns:
39+
// The Endorsement: A signature over the payload, and an identity that is used to verify the signature
40+
// The payload that was given as input (could be modified within this function)
41+
// Or error on failure
42+
func (e *DefaultESCC) Endorse(prpBytes []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) {
43+
signer, err := e.SigningIdentityForRequest(sp)
44+
if err != nil {
45+
return nil, nil, errors.New(fmt.Sprintf("failed fetching signing identity: %v", err))
46+
}
47+
// serialize the signing identity
48+
identityBytes, err := signer.Serialize()
49+
if err != nil {
50+
return nil, nil, errors.New(fmt.Sprintf("could not serialize the signing identity: %v", err))
51+
}
52+
53+
// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
54+
signature, err := signer.Sign(append(prpBytes, identityBytes...))
55+
if err != nil {
56+
return nil, nil, errors.New(fmt.Sprintf("could not sign the proposal response payload: %v", err))
57+
}
58+
endorsement := &peer.Endorsement{Signature: signature, Endorser: identityBytes}
59+
return endorsement, prpBytes, nil
60+
}
61+
62+
// Init injects dependencies into the instance of the Plugin
63+
func (e *DefaultESCC) Init(dependencies ...endorsement2.Dependency) error {
64+
for _, dep := range dependencies {
65+
sIDFetcher, isSigningIdentityFetcher := dep.(endorsement3.SigningIdentityFetcher)
66+
if !isSigningIdentityFetcher {
67+
continue
68+
}
69+
e.SigningIdentityFetcher = sIDFetcher
70+
return nil
71+
}
72+
return errors.New("could not find SigningIdentityFetcher in dependencies")
73+
}
74+
75+
// NewPluginFactory is the function ran by the plugin infrastructure to create an endorsement plugin factory.
76+
func NewPluginFactory() endorsement2.PluginFactory {
77+
return &DefaultESCCFactory{}
78+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package main
8+
9+
import (
10+
"github.com/hyperledger/fabric/core/handlers/endorsement/api"
11+
"github.com/hyperledger/fabric/protos/peer"
12+
)
13+
14+
type NoOpEndorser struct {
15+
}
16+
17+
func (*NoOpEndorser) Endorse(payload []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) {
18+
return nil, payload, nil
19+
}
20+
21+
func (*NoOpEndorser) Init(dependencies ...endorsement.Dependency) error {
22+
return nil
23+
}
24+
25+
type NoOpEndorserFactory struct {
26+
}
27+
28+
func (*NoOpEndorserFactory) New() endorsement.Plugin {
29+
return &NoOpEndorser{}
30+
}
31+
32+
// NewPluginFactory is the function ran by the plugin infrastructure to create an endorsement plugin factory.
33+
func NewPluginFactory() endorsement.PluginFactory {
34+
return &NoOpEndorserFactory{}
35+
}

core/handlers/library/library.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/hyperledger/fabric/core/handlers/auth/filter"
1212
"github.com/hyperledger/fabric/core/handlers/decoration"
1313
"github.com/hyperledger/fabric/core/handlers/decoration/decorator"
14+
endorsement "github.com/hyperledger/fabric/core/handlers/endorsement/api"
15+
"github.com/hyperledger/fabric/core/handlers/endorsement/builtin"
1416
)
1517

1618
// HandlerLibrary is used to assert
@@ -39,3 +41,7 @@ func (r *HandlerLibrary) ExpirationCheck() auth.Filter {
3941
func (r *HandlerLibrary) DefaultDecorator() decoration.Decorator {
4042
return decorator.NewDecorator()
4143
}
44+
45+
func (r *HandlerLibrary) ESCC() endorsement.PluginFactory {
46+
return &builtin.DefaultESCCFactory{}
47+
}

core/handlers/library/registry.go

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ import (
1313
"reflect"
1414
"sync"
1515

16+
"github.com/hyperledger/fabric/common/flogging"
1617
"github.com/hyperledger/fabric/core/handlers/auth"
1718
"github.com/hyperledger/fabric/core/handlers/decoration"
19+
endorsement2 "github.com/hyperledger/fabric/core/handlers/endorsement/api"
1820
)
1921

22+
var logger = flogging.MustGetLogger("core/handlers")
23+
2024
// Registry defines an object that looks up
2125
// handlers by name
2226
type Registry interface {
@@ -35,14 +39,17 @@ const (
3539
// Decoration handler - append or mutate the chaincode input
3640
// passed to the chaincode
3741
Decoration
42+
Endorsement
3843

39-
authPluginFactory = "NewFilter"
40-
decoratorPluginFactory = "NewDecorator"
44+
authPluginFactory = "NewFilter"
45+
decoratorPluginFactory = "NewDecorator"
46+
endorsementPluginFactory = "NewPluginFactory"
4147
)
4248

4349
type registry struct {
4450
filters []auth.Filter
4551
decorators []decoration.Decorator
52+
endorsers map[string]endorsement2.PluginFactory
4653
}
4754

4855
var once sync.Once
@@ -53,8 +60,11 @@ var reg registry
5360
type Config struct {
5461
AuthFilters []*HandlerConfig `mapstructure:"authFilters" yaml:"authFilters"`
5562
Decorators []*HandlerConfig `mapstructure:"decorators" yaml:"decorators"`
63+
Endorsers PluginMapping `mapstructure:"endorsers" yaml:"endorsers"`
5664
}
5765

66+
type PluginMapping map[string]*HandlerConfig
67+
5868
// HandlerConfig defines configuration for a plugin or compiled handler
5969
type HandlerConfig struct {
6070
Name string `mapstructure:"name" yaml:"name"`
@@ -65,7 +75,7 @@ type HandlerConfig struct {
6575
// of the registry
6676
func InitRegistry(c Config) Registry {
6777
once.Do(func() {
68-
reg = registry{}
78+
reg = registry{endorsers: make(map[string]endorsement2.PluginFactory)}
6979
reg.loadHandlers(c)
7080
})
7181
return &reg
@@ -79,24 +89,28 @@ func (r *registry) loadHandlers(c Config) {
7989
for _, config := range c.Decorators {
8090
r.evaluateModeAndLoad(config, Decoration)
8191
}
92+
93+
for chaincodeID, config := range c.Endorsers {
94+
r.evaluateModeAndLoad(config, Endorsement, chaincodeID)
95+
}
8296
}
8397

8498
// evaluateModeAndLoad if a library path is provided, load the shared object
85-
func (r *registry) evaluateModeAndLoad(c *HandlerConfig, handlerType HandlerType) {
99+
func (r *registry) evaluateModeAndLoad(c *HandlerConfig, handlerType HandlerType, extraArgs ...string) {
86100
if c.Library != "" {
87-
r.loadPlugin(c.Library, handlerType)
101+
r.loadPlugin(c.Library, handlerType, extraArgs...)
88102
} else {
89-
r.loadCompiled(c.Name, handlerType)
103+
r.loadCompiled(c.Name, handlerType, extraArgs...)
90104
}
91105
}
92106

93107
// loadCompiled loads a statically compiled handler
94-
func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType) {
108+
func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType, extraArgs ...string) {
95109
registryMD := reflect.ValueOf(&HandlerLibrary{})
96110

97111
o := registryMD.MethodByName(handlerFactory)
98112
if !o.IsValid() {
99-
panic(fmt.Errorf("Method %s isn't a method of HandlerLibrary", handlerFactory))
113+
logger.Panicf(fmt.Sprintf("Method %s isn't a method of HandlerLibrary", handlerFactory))
100114
}
101115

102116
inst := o.Call(nil)[0].Interface()
@@ -105,24 +119,30 @@ func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType)
105119
r.filters = append(r.filters, inst.(auth.Filter))
106120
} else if handlerType == Decoration {
107121
r.decorators = append(r.decorators, inst.(decoration.Decorator))
122+
} else if handlerType == Endorsement {
123+
if len(extraArgs) != 1 {
124+
logger.Panicf("expected 1 argument in extraArgs")
125+
}
126+
r.endorsers[extraArgs[0]] = inst.(endorsement2.PluginFactory)
108127
}
109128
}
110129

111130
// loadPlugin loads a pluggagle handler
112-
func (r *registry) loadPlugin(pluginPath string, handlerType HandlerType) {
131+
func (r *registry) loadPlugin(pluginPath string, handlerType HandlerType, extraArgs ...string) {
113132
if _, err := os.Stat(pluginPath); err != nil {
114-
panic(fmt.Errorf("Could not find plugin at path %s: %s", pluginPath, err))
133+
logger.Panicf(fmt.Sprintf("Could not find plugin at path %s: %s", pluginPath, err))
115134
}
116-
117135
p, err := plugin.Open(pluginPath)
118136
if err != nil {
119-
panic(fmt.Errorf("Error opening plugin at path %s: %s", pluginPath, err))
137+
logger.Panicf(fmt.Sprintf("Error opening plugin at path %s: %s", pluginPath, err))
120138
}
121139

122140
if handlerType == Auth {
123141
r.initAuthPlugin(p)
124142
} else if handlerType == Decoration {
125143
r.initDecoratorPlugin(p)
144+
} else if handlerType == Endorsement {
145+
r.initEndorsementPlugin(p, extraArgs...)
126146
}
127147
}
128148

@@ -159,17 +179,37 @@ func (r *registry) initDecoratorPlugin(p *plugin.Plugin) {
159179
}
160180
}
161181

182+
func (r *registry) initEndorsementPlugin(p *plugin.Plugin, extraArgs ...string) {
183+
if len(extraArgs) != 1 {
184+
logger.Panicf("expected 1 argument in extraArgs")
185+
}
186+
factorySymbol, err := p.Lookup(endorsementPluginFactory)
187+
if err != nil {
188+
panicWithLookupError(endorsementPluginFactory, err)
189+
}
190+
191+
constructor, ok := factorySymbol.(func() endorsement2.PluginFactory)
192+
if !ok {
193+
panicWithDefinitionError(endorsementPluginFactory)
194+
}
195+
factory := constructor()
196+
if factory == nil {
197+
logger.Panicf("factory instance returned nil")
198+
}
199+
r.endorsers[extraArgs[0]] = factory
200+
}
201+
162202
// panicWithLookupError panics when a handler constructor lookup fails
163203
func panicWithLookupError(factory string, err error) {
164-
panic(fmt.Errorf("Filter must contain constructor with name %s. Error from lookup: %s",
204+
logger.Panicf(fmt.Sprintf("Plugin must contain constructor with name %s. Error from lookup: %s",
165205
factory, err))
166206
}
167207

168208
// panicWithDefinitionError panics when a handler constructor does not match
169209
// the expected function definition
170210
func panicWithDefinitionError(factory string) {
171-
panic(fmt.Errorf("Constructor method %s does not match expected definition",
172-
authPluginFactory))
211+
logger.Panicf(fmt.Sprintf("Constructor method %s does not match expected definition",
212+
factory))
173213
}
174214

175215
// Lookup returns a list of handlers with the given
@@ -179,6 +219,8 @@ func (r *registry) Lookup(handlerType HandlerType) interface{} {
179219
return r.filters
180220
} else if handlerType == Decoration {
181221
return r.decorators
222+
} else if handlerType == Endorsement {
223+
return r.endorsers
182224
}
183225

184226
return nil

0 commit comments

Comments
 (0)