Skip to content

Commit

Permalink
[FAB-9724] Validation plugin infrastructure support
Browse files Browse the repository at this point in the history
This change set adds support to the handlers to load validation plugins.

Change-Id: I1cbf29bfe8899a43569a67d830c21d6f3b67d833
Signed-off-by: yacovm <yacovm@il.ibm.com>
  • Loading branch information
yacovm committed May 10, 2018
1 parent fc733e6 commit 1240736
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 7 deletions.
54 changes: 47 additions & 7 deletions core/handlers/library/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hyperledger/fabric/core/handlers/auth"
"github.com/hyperledger/fabric/core/handlers/decoration"
endorsement2 "github.com/hyperledger/fabric/core/handlers/endorsement/api"
"github.com/hyperledger/fabric/core/handlers/validation/api"
)

var logger = flogging.MustGetLogger("core/handlers")
Expand All @@ -40,16 +41,18 @@ const (
// passed to the chaincode
Decoration
Endorsement
Validation

authPluginFactory = "NewFilter"
decoratorPluginFactory = "NewDecorator"
endorsementPluginFactory = "NewPluginFactory"
authPluginFactory = "NewFilter"
decoratorPluginFactory = "NewDecorator"
pluginFactory = "NewPluginFactory"
)

type registry struct {
filters []auth.Filter
decorators []decoration.Decorator
endorsers map[string]endorsement2.PluginFactory
validators map[string]validation.PluginFactory
}

var once sync.Once
Expand All @@ -61,6 +64,7 @@ type Config struct {
AuthFilters []*HandlerConfig `mapstructure:"authFilters" yaml:"authFilters"`
Decorators []*HandlerConfig `mapstructure:"decorators" yaml:"decorators"`
Endorsers PluginMapping `mapstructure:"endorsers" yaml:"endorsers"`
Validators PluginMapping `mapstructure:"validators" yaml:"validators"`
}

type PluginMapping map[string]*HandlerConfig
Expand All @@ -75,7 +79,10 @@ type HandlerConfig struct {
// of the registry
func InitRegistry(c Config) Registry {
once.Do(func() {
reg = registry{endorsers: make(map[string]endorsement2.PluginFactory)}
reg = registry{
endorsers: make(map[string]endorsement2.PluginFactory),
validators: make(map[string]validation.PluginFactory),
}
reg.loadHandlers(c)
})
return &reg
Expand All @@ -93,6 +100,10 @@ func (r *registry) loadHandlers(c Config) {
for chaincodeID, config := range c.Endorsers {
r.evaluateModeAndLoad(config, Endorsement, chaincodeID)
}

for chaincodeID, config := range c.Validators {
r.evaluateModeAndLoad(config, Validation, chaincodeID)
}
}

// evaluateModeAndLoad if a library path is provided, load the shared object
Expand Down Expand Up @@ -124,6 +135,11 @@ func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType,
logger.Panicf("expected 1 argument in extraArgs")
}
r.endorsers[extraArgs[0]] = inst.(endorsement2.PluginFactory)
} else if handlerType == Validation {
if len(extraArgs) != 1 {
logger.Panicf("expected 1 argument in extraArgs")
}
r.validators[extraArgs[0]] = inst.(validation.PluginFactory)
}
}

Expand All @@ -143,6 +159,8 @@ func (r *registry) loadPlugin(pluginPath string, handlerType HandlerType, extraA
r.initDecoratorPlugin(p)
} else if handlerType == Endorsement {
r.initEndorsementPlugin(p, extraArgs...)
} else if handlerType == Validation {
r.initValidationPlugin(p, extraArgs...)
}
}

Expand Down Expand Up @@ -183,14 +201,14 @@ func (r *registry) initEndorsementPlugin(p *plugin.Plugin, extraArgs ...string)
if len(extraArgs) != 1 {
logger.Panicf("expected 1 argument in extraArgs")
}
factorySymbol, err := p.Lookup(endorsementPluginFactory)
factorySymbol, err := p.Lookup(pluginFactory)
if err != nil {
panicWithLookupError(endorsementPluginFactory, err)
panicWithLookupError(pluginFactory, err)
}

constructor, ok := factorySymbol.(func() endorsement2.PluginFactory)
if !ok {
panicWithDefinitionError(endorsementPluginFactory)
panicWithDefinitionError(pluginFactory)
}
factory := constructor()
if factory == nil {
Expand All @@ -199,6 +217,26 @@ func (r *registry) initEndorsementPlugin(p *plugin.Plugin, extraArgs ...string)
r.endorsers[extraArgs[0]] = factory
}

func (r *registry) initValidationPlugin(p *plugin.Plugin, extraArgs ...string) {
if len(extraArgs) != 1 {
logger.Panicf("expected 1 argument in extraArgs")
}
factorySymbol, err := p.Lookup(pluginFactory)
if err != nil {
panicWithLookupError(pluginFactory, err)
}

constructor, ok := factorySymbol.(func() validation.PluginFactory)
if !ok {
panicWithDefinitionError(pluginFactory)
}
factory := constructor()
if factory == nil {
logger.Panicf("factory instance returned nil")
}
r.validators[extraArgs[0]] = factory
}

// panicWithLookupError panics when a handler constructor lookup fails
func panicWithLookupError(factory string, err error) {
logger.Panicf(fmt.Sprintf("Plugin must contain constructor with name %s. Error from lookup: %s",
Expand All @@ -221,6 +259,8 @@ func (r *registry) Lookup(handlerType HandlerType) interface{} {
return r.decorators
} else if handlerType == Endorsement {
return r.endorsers
} else if handlerType == Validation {
return r.validators
}

return nil
Expand Down
26 changes: 26 additions & 0 deletions core/handlers/library/registry_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/handlers/endorsement/api"
"github.com/hyperledger/fabric/core/handlers/validation/api"
"github.com/hyperledger/fabric/protos/peer"
"github.com/stretchr/testify/assert"
)
Expand All @@ -28,6 +29,7 @@ const (
authPluginPackage = "github.com/hyperledger/fabric/core/handlers/auth/plugin"
decoratorPluginPackage = "github.com/hyperledger/fabric/core/handlers/decoration/plugin"
endorsementTestPlugin = "github.com/hyperledger/fabric/core/handlers/endorsement/testdata/"
validationTestPlugin = "github.com/hyperledger/fabric/core/handlers/validation/testdata/"
)

func TestLoadAuthPlugin(t *testing.T) {
Expand Down Expand Up @@ -99,6 +101,30 @@ func TestEndorsementPlugin(t *testing.T) {
assert.Equal(t, []byte{1, 2, 3}, output)
}

func TestValidationPlugin(t *testing.T) {
testDir, err := ioutil.TempDir("", "")
assert.NoError(t, err, "Could not create temp directory for plugins")
defer os.Remove(testDir)

pluginPath := strings.Join([]string{testDir, "/", "validationplugin.so"}, "")

cmd := exec.Command("go", "build", "-o", pluginPath, "-buildmode=plugin",
validationTestPlugin)
output, err := cmd.CombinedOutput()
assert.NoError(t, err, "Could not build plugin: "+string(output))

testReg := registry{validators: make(map[string]validation.PluginFactory)}
testReg.loadPlugin(pluginPath, Validation, "vscc")
mapping := testReg.Lookup(Validation).(map[string]validation.PluginFactory)
factory := mapping["vscc"]
assert.NotNil(t, factory)
instance := factory.New()
assert.NotNil(t, instance)
assert.NoError(t, instance.Init())
err = instance.Validate(nil, "", 0, 0)
assert.NoError(t, err)
}

func TestLoadPluginInvalidPath(t *testing.T) {
defer func() {
if r := recover(); r == nil {
Expand Down
41 changes: 41 additions & 0 deletions core/handlers/validation/testdata/noop_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package main

import (
"github.com/hyperledger/fabric/core/handlers/validation/api"
"github.com/hyperledger/fabric/protos/common"
)

// NoOpValidator is used to test validation plugin infrastructure
type NoOpValidator struct {
}

// Validate valides the transactions with the given data
func (*NoOpValidator) Validate(_ *common.Block, _ string, _ int, _ int, _ ...validation.ContextDatum) error {
return nil
}

// Init initializes the plugin with the given dependencies
func (*NoOpValidator) Init(dependencies ...validation.Dependency) error {
return nil
}

// NoOpValidatorFactory creates new NoOpValidators
type NoOpValidatorFactory struct {
}

// New returns an instance of a NoOpValidator
func (*NoOpValidatorFactory) New() validation.Plugin {
return &NoOpValidator{}
}

// NewPluginFactory is called by the validation plugin framework to obtain an instance
// of the factory
func NewPluginFactory() validation.PluginFactory {
return &NoOpValidatorFactory{}
}

0 comments on commit 1240736

Please sign in to comment.