Skip to content

Commit 81641a9

Browse files
committed
[FAB-6719] Allow system chaincode plugins
Change-Id: I2db25bdfd8ebffbcb22c3d7374364f6d2d8a6fd6 Signed-off-by: Divyank Katira <Divyank.Katira@securekey.com>
1 parent aa69bc7 commit 81641a9

File tree

8 files changed

+227
-2
lines changed

8 files changed

+227
-2
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// +build experimental
2+
3+
/*
4+
Copyright SecureKey Technologies Inc. All Rights Reserved.
5+
6+
SPDX-License-Identifier: Apache-2.0
7+
*/
8+
9+
package scc
10+
11+
var goBuildTags = "experimental"

core/scc/buildtags_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// +build !experimental
2+
3+
/*
4+
Copyright SecureKey Technologies Inc. All Rights Reserved.
5+
6+
SPDX-License-Identifier: Apache-2.0
7+
*/
8+
9+
package scc
10+
11+
var goBuildTags = ""

core/scc/importsysccs.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ var systemChaincodes = []*SystemChaincode{
8484
//RegisterSysCCs is the hook for system chaincodes where system chaincodes are registered with the fabric
8585
//note the chaincode must still be deployed and launched like a user chaincode will be
8686
func RegisterSysCCs() {
87+
systemChaincodes = append(systemChaincodes, loadSysCCs()...)
88+
8789
var aclProvider aclmgmt.ACLProvider
8890
for _, sysCC := range systemChaincodes {
8991
if reg, _ := registerSysCC(sysCC); reg {

core/scc/loadsysccs.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright SecureKey Technologies Inc. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package scc
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"plugin"
13+
"sync"
14+
15+
"github.com/hyperledger/fabric/common/viperutil"
16+
"github.com/hyperledger/fabric/core/chaincode/shim"
17+
"github.com/pkg/errors"
18+
)
19+
20+
const (
21+
sccFactoryMethod = "New"
22+
)
23+
24+
// PluginConfig SCC plugin configuration
25+
type PluginConfig struct {
26+
Enabled bool `mapstructure:"enabled" yaml:"enabled"`
27+
Name string `mapstructure:"name" yaml:"name"`
28+
Path string `mapstructure:"path" yaml:"path"`
29+
InvokableExternal bool `mapstructure:"invokableExternal" yaml:"invokableExternal"`
30+
InvokableCC2CC bool `mapstructure:"invokableCC2CC" yaml:"invokableCC2CC"`
31+
}
32+
33+
var once sync.Once
34+
var sccPlugins []*SystemChaincode
35+
36+
// loadSysCCs reads system chaincode plugin configuration and loads them
37+
func loadSysCCs() []*SystemChaincode {
38+
once.Do(func() {
39+
var config []*PluginConfig
40+
err := viperutil.EnhancedExactUnmarshalKey("chaincode.systemPlugins", &config)
41+
if err != nil {
42+
panic(errors.WithMessage(err, "could not load YAML config"))
43+
}
44+
loadSysCCsWithConfig(config)
45+
})
46+
return sccPlugins
47+
}
48+
49+
func loadSysCCsWithConfig(configs []*PluginConfig) {
50+
for _, conf := range configs {
51+
plugin := loadPlugin(conf.Path)
52+
chaincode := &SystemChaincode{
53+
Enabled: conf.Enabled,
54+
Name: conf.Name,
55+
Path: conf.Path,
56+
Chaincode: *plugin,
57+
InvokableExternal: conf.InvokableExternal,
58+
InvokableCC2CC: conf.InvokableCC2CC,
59+
}
60+
sccPlugins = append(sccPlugins, chaincode)
61+
sysccLogger.Infof("Successfully loaded SCC %s from path %s", chaincode.Name, chaincode.Path)
62+
}
63+
}
64+
65+
func loadPlugin(path string) *shim.Chaincode {
66+
if _, err := os.Stat(path); err != nil {
67+
panic(fmt.Errorf("Could not find plugin at path %s: %s", path, err))
68+
}
69+
70+
p, err := plugin.Open(path)
71+
if err != nil {
72+
panic(fmt.Errorf("Error opening plugin at path %s: %s", path, err))
73+
}
74+
75+
sccFactorySymbol, err := p.Lookup(sccFactoryMethod)
76+
if err != nil {
77+
panic(fmt.Errorf(
78+
"Could not find symbol %s. Plugin must export this method", sccFactoryMethod))
79+
}
80+
81+
sccFactory, ok := sccFactorySymbol.(func() shim.Chaincode)
82+
if !ok {
83+
panic(fmt.Errorf("Function %s does not match expected definition func() shim.Chaincode",
84+
sccFactoryMethod))
85+
}
86+
87+
scc := sccFactory()
88+
89+
return &scc
90+
}

core/scc/loadsysccs_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// +build go1.9,linux,cgo
2+
// +build !ppc64le
3+
4+
/*
5+
Copyright SecureKey Technologies Inc. All Rights Reserved.
6+
7+
SPDX-License-Identifier: Apache-2.0
8+
*/
9+
10+
package scc
11+
12+
import (
13+
"bytes"
14+
"fmt"
15+
"os"
16+
"os/exec"
17+
"testing"
18+
19+
"github.com/hyperledger/fabric/core/chaincode/shim"
20+
"github.com/spf13/viper"
21+
"github.com/stretchr/testify/assert"
22+
)
23+
24+
const (
25+
examplePluginPackage = "github.com/hyperledger/fabric/examples/plugins/scc"
26+
pluginName = "testscc"
27+
)
28+
29+
var pluginPath = os.TempDir() + "/scc-plugin.so"
30+
31+
func TestLoadSCCPlugin(t *testing.T) {
32+
buildExamplePlugin(pluginPath, examplePluginPackage)
33+
defer os.Remove(pluginPath)
34+
35+
testConfig := fmt.Sprintf(`
36+
chaincode:
37+
systemPlugins:
38+
- enabled: true
39+
name: %s
40+
path: %s
41+
invokableExternal: true
42+
invokableCC2CC: true
43+
`, pluginName, pluginPath)
44+
viper.SetConfigType("yaml")
45+
viper.ReadConfig(bytes.NewBuffer([]byte(testConfig)))
46+
47+
sccs := loadSysCCs()
48+
assert.Len(t, sccs, 1, "expected one SCC to be loaded")
49+
resp := sccs[0].Chaincode.Invoke(nil)
50+
assert.Equal(t, int32(shim.OK), resp.Status, "expected success response from scc")
51+
}
52+
53+
func TestLoadSCCPluginInvalid(t *testing.T) {
54+
assert.Panics(t, func() { loadPlugin("/invalid/path.so") },
55+
"expected panic with invalid path")
56+
}
57+
58+
func buildExamplePlugin(path, pluginPackage string) {
59+
cmd := exec.Command("go", "build", "-tags", goBuildTags, "-o", path, "-buildmode=plugin",
60+
pluginPackage)
61+
output, err := cmd.CombinedOutput()
62+
if err != nil {
63+
panic(fmt.Errorf("Error: %s, Could not build plugin: %s", err, string(output)))
64+
}
65+
}

core/scc/scc_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ func init() {
3333
viper.Set("chaincode.system", map[string]string{"lscc": "enable", "a": "enable"})
3434
viper.Set("peer.fileSystemPath", os.TempDir())
3535
ccprovider.RegisterChaincodeProviderFactory(&ccprovider2.MockCcProviderFactory{})
36-
RegisterSysCCs()
3736
}
3837

3938
func TestDeploy(t *testing.T) {
@@ -103,6 +102,8 @@ func TestMockRegisterAndResetSysCCs(t *testing.T) {
103102
}
104103

105104
func TestRegisterSysCC(t *testing.T) {
105+
assert.NotPanics(t, func() { RegisterSysCCs() }, "expected successful init")
106+
106107
_, err := registerSysCC(&SystemChaincode{
107108
Name: "lscc",
108109
Path: "path",

examples/plugins/scc/plugin.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright SecureKey Technologies Inc. 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/chaincode/shim"
11+
pb "github.com/hyperledger/fabric/protos/peer"
12+
)
13+
14+
// New returns an implementation of the chaincode interface
15+
func New() shim.Chaincode {
16+
return &scc{}
17+
}
18+
19+
type scc struct{}
20+
21+
// Init implements the chaincode shim interface
22+
func (s *scc) Init(stub shim.ChaincodeStubInterface) pb.Response {
23+
return shim.Success(nil)
24+
}
25+
26+
// Invoke implements the chaincode shim interface
27+
func (s *scc) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
28+
return shim.Success(nil)
29+
}
30+
31+
func main() {}

sampleconfig/core.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,20 @@ chaincode:
446446
qscc: enable
447447
rscc: disable
448448

449+
# System chaincode plugins: in addition to being imported and compiled
450+
# into fabric through core/chaincode/importsysccs.go, system chaincodes
451+
# can also be loaded as shared objects compiled as Go plugins.
452+
# See examples/plugins/scc for an example.
453+
# Like regular system chaincodes, plugins must also be white listed in the
454+
# chaincode.system section above.
455+
systemPlugins:
456+
# example configuration:
457+
# - enabled: true
458+
# name: myscc
459+
# path: /opt/lib/myscc.so
460+
# invokableExternal: true
461+
# invokableCC2CC: true
462+
449463
# Logging section for the chaincode container
450464
logging:
451465
# Default level for all loggers within the chaincode container
@@ -534,4 +548,4 @@ metrics:
534548
promReporter:
535549

536550
# prometheus http server listen address for pull metrics
537-
listenAddress: 0.0.0.0:8080
551+
listenAddress: 0.0.0.0:8080

0 commit comments

Comments
 (0)