/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package fabenc
import (
"fmt"
)
type Color uint8
const ColorNone Color = 0
const (
ColorBlack Color = iota + 30
ColorRed
ColorGreen
ColorYellow
ColorBlue
ColorMagenta
ColorCyan
ColorWhite
)
func (c Color) Normal() string {
return fmt.Sprintf("\x1b[%dm", c)
}
func (c Color) Bold() string {
if c == ColorNone {
return c.Normal()
}
return fmt.Sprintf("\x1b[%d;1m", c)
}
func ResetColor() string { return ColorNone.Normal() }
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package fabenc
import (
"io"
"time"
zaplogfmt "github.com/sykesm/zap-logfmt"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
)
// A FormatEncoder is a zapcore.Encoder that formats log records according to a
// go-logging based format specifier.
type FormatEncoder struct {
zapcore.Encoder
formatters []Formatter
pool buffer.Pool
}
// A Formatter is used to format and write data from a zap log entry.
type Formatter interface {
Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field)
}
func NewFormatEncoder(formatters ...Formatter) *FormatEncoder {
return &FormatEncoder{
Encoder: zaplogfmt.NewEncoder(zapcore.EncoderConfig{
MessageKey: "", // disable
LevelKey: "", // disable
TimeKey: "", // disable
NameKey: "", // disable
CallerKey: "", // disable
StacktraceKey: "", // disable
LineEnding: "\n",
EncodeDuration: zapcore.StringDurationEncoder,
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02T15:04:05.999Z07:00"))
},
}),
formatters: formatters,
pool: buffer.NewPool(),
}
}
// Clone creates a new instance of this encoder with the same configuration.
func (f *FormatEncoder) Clone() zapcore.Encoder {
return &FormatEncoder{
Encoder: f.Encoder.Clone(),
formatters: f.formatters,
pool: f.pool,
}
}
// EncodeEntry formats a zap log record. The structured fields are formatted by a
// zapcore.ConsoleEncoder and are appended as JSON to the end of the formatted entry.
// All entries are terminated by a newline.
func (f *FormatEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
line := f.pool.Get()
for _, f := range f.formatters {
f.Format(line, entry, fields)
}
encodedFields, err := f.Encoder.EncodeEntry(entry, fields)
if err != nil {
return nil, err
}
if line.Len() > 0 && encodedFields.Len() != 1 {
line.AppendString(" ")
}
line.AppendString(encodedFields.String())
encodedFields.Free()
return line, nil
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fabenc
func FuzzParseFormat(data []byte) int {
_, _ = ParseFormat(string(data))
return 1
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package fabenc
import (
"fmt"
"io"
"regexp"
"runtime"
"strings"
"sync"
"sync/atomic"
"go.uber.org/zap/zapcore"
)
// formatRegexp is broken into three groups:
// 1. the format verb
// 2. an optional colon that is ungrouped with '?:'
// 3. an optional, non-greedy format directive
//
// The grouping simplifies the verb proccssing during spec parsing.
var formatRegexp = regexp.MustCompile(`%{(color|id|level|message|module|shortfunc|time)(?::(.*?))?}`)
// ParseFormat parses a log format spec and returns a slice of formatters
// that should be iterated over to build a formatted log record.
//
// The op-loggng specifiers supported by this formatter are:
// - %{color} - level specific SGR color escape or SGR reset
// - %{id} - a unique log sequence number
// - %{level} - the log level of the entry
// - %{message} - the log message
// - %{module} - the zap logger name
// - %{shortfunc} - the name of the function creating the log record
// - %{time} - the time the log entry was created
//
// Specifiers may include an optional format verb:
// - color: reset|bold
// - id: a fmt style numeric formatter without the leading %
// - level: a fmt style string formatter without the leading %
// - message: a fmt style string formatter without the leading %
// - module: a fmt style string formatter without the leading %
func ParseFormat(spec string) ([]Formatter, error) {
cursor := 0
formatters := []Formatter{}
// iterate over the regex groups and convert to formatters
matches := formatRegexp.FindAllStringSubmatchIndex(spec, -1)
for _, m := range matches {
start, end := m[0], m[1]
verbStart, verbEnd := m[2], m[3]
formatStart, formatEnd := m[4], m[5]
if start > cursor {
formatters = append(formatters, StringFormatter{Value: spec[cursor:start]})
}
var format string
if formatStart >= 0 {
format = spec[formatStart:formatEnd]
}
formatter, err := NewFormatter(spec[verbStart:verbEnd], format)
if err != nil {
return nil, err
}
formatters = append(formatters, formatter)
cursor = end
}
// handle any trailing suffix
if cursor != len(spec) {
formatters = append(formatters, StringFormatter{Value: spec[cursor:]})
}
return formatters, nil
}
// A MultiFormatter presents multiple formatters as a single Formatter. It can
// be used to change the set of formatters associated with an encoder at
// runtime.
type MultiFormatter struct {
mutex sync.RWMutex
formatters []Formatter
}
// NewMultiFormatter creates a new MultiFormatter that delegates to the
// provided formatters. The formatters are used in the order they are
// presented.
func NewMultiFormatter(formatters ...Formatter) *MultiFormatter {
return &MultiFormatter{
formatters: formatters,
}
}
// Format iterates over its delegates to format a log record to the provided
// buffer.
func (m *MultiFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
m.mutex.RLock()
for i := range m.formatters {
m.formatters[i].Format(w, entry, fields)
}
m.mutex.RUnlock()
}
// SetFormatters replaces the delegate formatters.
func (m *MultiFormatter) SetFormatters(formatters []Formatter) {
m.mutex.Lock()
m.formatters = formatters
m.mutex.Unlock()
}
// A StringFormatter formats a fixed string.
type StringFormatter struct{ Value string }
// Format writes the formatter's fixed string to provided writer.
func (s StringFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, "%s", s.Value)
}
// NewFormatter creates the formatter for the provided verb. When a format is
// not provided, the default format for the verb is used.
func NewFormatter(verb, format string) (Formatter, error) {
switch verb {
case "color":
return newColorFormatter(format)
case "id":
return newSequenceFormatter(format), nil
case "level":
return newLevelFormatter(format), nil
case "message":
return newMessageFormatter(format), nil
case "module":
return newModuleFormatter(format), nil
case "shortfunc":
return newShortFuncFormatter(format), nil
case "time":
return newTimeFormatter(format), nil
default:
return nil, fmt.Errorf("unknown verb: %s", verb)
}
}
// A ColorFormatter formats an SGR color code.
type ColorFormatter struct {
Bold bool // set the bold attribute
Reset bool // reset colors and attributes
}
func newColorFormatter(f string) (ColorFormatter, error) {
switch f {
case "bold":
return ColorFormatter{Bold: true}, nil
case "reset":
return ColorFormatter{Reset: true}, nil
case "":
return ColorFormatter{}, nil
default:
return ColorFormatter{}, fmt.Errorf("invalid color option: %s", f)
}
}
// LevelColor returns the Color associated with a specific zap logging level.
func (c ColorFormatter) LevelColor(l zapcore.Level) Color {
switch l {
case zapcore.DebugLevel:
return ColorCyan
case zapcore.InfoLevel:
return ColorBlue
case zapcore.WarnLevel:
return ColorYellow
case zapcore.ErrorLevel:
return ColorRed
case zapcore.DPanicLevel, zapcore.PanicLevel:
return ColorMagenta
case zapcore.FatalLevel:
return ColorMagenta
default:
return ColorNone
}
}
// Format writes the SGR color code to the provided writer.
func (c ColorFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
switch {
case c.Reset:
fmt.Fprint(w, ResetColor())
case c.Bold:
fmt.Fprint(w, c.LevelColor(entry.Level).Bold())
default:
fmt.Fprint(w, c.LevelColor(entry.Level).Normal())
}
}
// LevelFormatter formats a log level.
type LevelFormatter struct{ FormatVerb string }
func newLevelFormatter(f string) LevelFormatter {
return LevelFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the logging level to the provided writer.
func (l LevelFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, l.FormatVerb, entry.Level.CapitalString())
}
// MessageFormatter formats a log message.
type MessageFormatter struct{ FormatVerb string }
func newMessageFormatter(f string) MessageFormatter {
return MessageFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the log entry message to the provided writer.
func (m MessageFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, m.FormatVerb, strings.TrimRight(entry.Message, "\n"))
}
// ModuleFormatter formats the zap logger name.
type ModuleFormatter struct{ FormatVerb string }
func newModuleFormatter(f string) ModuleFormatter {
return ModuleFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the zap logger name to the specified writer.
func (m ModuleFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, m.FormatVerb, entry.LoggerName)
}
// sequence maintains the global sequence number shared by all SequeneFormatter
// instances.
var sequence uint64
// SetSequence explicitly sets the global sequence number.
func SetSequence(s uint64) { atomic.StoreUint64(&sequence, s) }
// SequenceFormatter formats a global sequence number.
type SequenceFormatter struct{ FormatVerb string }
func newSequenceFormatter(f string) SequenceFormatter {
return SequenceFormatter{FormatVerb: "%" + stringOrDefault(f, "d")}
}
// SequenceFormatter increments a global sequence number and writes it to the
// provided writer.
func (s SequenceFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, s.FormatVerb, atomic.AddUint64(&sequence, 1))
}
// ShortFuncFormatter formats the name of the function creating the log record.
type ShortFuncFormatter struct{ FormatVerb string }
func newShortFuncFormatter(f string) ShortFuncFormatter {
return ShortFuncFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the calling function name to the provided writer. The name is obtained from
// the runtime and the package and line numbers are discarded.
func (s ShortFuncFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
f := runtime.FuncForPC(entry.Caller.PC)
if f == nil {
fmt.Fprintf(w, s.FormatVerb, "(unknown)")
return
}
fname := f.Name()
funcIdx := strings.LastIndex(fname, ".")
fmt.Fprintf(w, s.FormatVerb, fname[funcIdx+1:])
}
// TimeFormatter formats the time from the zap log entry.
type TimeFormatter struct{ Layout string }
func newTimeFormatter(f string) TimeFormatter {
return TimeFormatter{Layout: stringOrDefault(f, "2006-01-02T15:04:05.999Z07:00")}
}
// Format writes the log record time stamp to the provided writer.
func (t TimeFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprint(w, entry.Time.Format(t.Layout))
}
func stringOrDefault(str, dflt string) string {
if str != "" {
return str
}
return dflt
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package cauthdsl
import (
"fmt"
"time"
"github.com/hyperledger/fabric-lib-go/common/flogging"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
mb "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/hyperledger/fabric/msp"
"go.uber.org/zap/zapcore"
)
var cauthdslLogger = flogging.MustGetLogger("cauthdsl")
// compile recursively builds a go evaluatable function corresponding to the policy specified, remember to call deduplicate on identities before
// passing them to this function for evaluation
func compile(policy *cb.SignaturePolicy, identities []*mb.MSPPrincipal) (func([]msp.Identity, []bool) bool, error) {
if policy == nil {
return nil, fmt.Errorf("Empty policy element")
}
switch t := policy.Type.(type) {
case *cb.SignaturePolicy_NOutOf_:
policies := make([]func([]msp.Identity, []bool) bool, len(t.NOutOf.Rules))
for i, policy := range t.NOutOf.Rules {
compiledPolicy, err := compile(policy, identities)
if err != nil {
return nil, err
}
policies[i] = compiledPolicy
}
return func(signedData []msp.Identity, used []bool) bool {
grepKey := time.Now().UnixNano()
cauthdslLogger.Debugf("%p gate %d evaluation starts", signedData, grepKey)
verified := int32(0)
_used := make([]bool, len(used))
for _, policy := range policies {
copy(_used, used)
if policy(signedData, _used) {
verified++
copy(used, _used)
}
}
if verified >= t.NOutOf.N {
cauthdslLogger.Debugf("%p gate %d evaluation succeeds", signedData, grepKey)
} else {
cauthdslLogger.Debugf("%p gate %d evaluation fails", signedData, grepKey)
}
return verified >= t.NOutOf.N
}, nil
case *cb.SignaturePolicy_SignedBy:
if t.SignedBy < 0 || t.SignedBy >= int32(len(identities)) {
return nil, fmt.Errorf("identity index out of range, requested %v, but identities length is %d", t.SignedBy, len(identities))
}
signedByID := identities[t.SignedBy]
return func(signedData []msp.Identity, used []bool) bool {
cauthdslLogger.Debugf("%p signed by %d principal evaluation starts (used %v)", signedData, t.SignedBy, used)
for i, sd := range signedData {
if used[i] {
cauthdslLogger.Debugf("%p skipping identity %d because it has already been used", signedData, i)
continue
}
if cauthdslLogger.IsEnabledFor(zapcore.DebugLevel) {
// Unlike most places, this is a huge print statement, and worth checking log level before create garbage
cauthdslLogger.Debugf("%p processing identity %d - %v", signedData, i, sd.GetIdentifier())
}
err := sd.SatisfiesPrincipal(signedByID)
if err != nil {
cauthdslLogger.Debugf("%p identity %d does not satisfy principal: %s", signedData, i, err)
continue
}
cauthdslLogger.Debugf("%p principal evaluation succeeds for identity %d", signedData, i)
used[i] = true
return true
}
cauthdslLogger.Debugf("%p principal evaluation fails", signedData)
return false
}, nil
default:
return nil, fmt.Errorf("Unknown type: %T:%v", t, t)
}
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package cauthdsl
import (
"fmt"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
type provider struct {
deserializer msp.IdentityDeserializer
}
// NewPolicyProvider provides a policy generator for cauthdsl type policies
func NewPolicyProvider(deserializer msp.IdentityDeserializer) policies.Provider {
return &provider{
deserializer: deserializer,
}
}
// NewPolicy creates a new policy based on the policy bytes
func (pr *provider) NewPolicy(data []byte) (policies.Policy, proto.Message, error) {
sigPolicy := &cb.SignaturePolicyEnvelope{}
if err := proto.Unmarshal(data, sigPolicy); err != nil {
return nil, nil, fmt.Errorf("Error unmarshalling to SignaturePolicy: %s", err)
}
if sigPolicy.Version != 0 {
return nil, nil, fmt.Errorf("This evaluator only understands messages of version 0, but version was %d", sigPolicy.Version)
}
compiled, err := compile(sigPolicy.Rule, sigPolicy.Identities)
if err != nil {
return nil, nil, err
}
return &policy{
evaluator: compiled,
deserializer: pr.deserializer,
signaturePolicyEnvelope: sigPolicy,
}, sigPolicy, nil
}
// EnvelopeBasedPolicyProvider allows to create a new policy from SignaturePolicyEnvelope struct instead of []byte
type EnvelopeBasedPolicyProvider struct {
Deserializer msp.IdentityDeserializer
}
// NewPolicy creates a new policy from the policy envelope
func (pp *EnvelopeBasedPolicyProvider) NewPolicy(sigPolicy *cb.SignaturePolicyEnvelope) (policies.Policy, error) {
if sigPolicy == nil {
return nil, errors.New("invalid arguments")
}
compiled, err := compile(sigPolicy.Rule, sigPolicy.Identities)
if err != nil {
return nil, err
}
return &policy{
evaluator: compiled,
deserializer: pp.Deserializer,
signaturePolicyEnvelope: sigPolicy,
}, nil
}
type policy struct {
signaturePolicyEnvelope *cb.SignaturePolicyEnvelope
evaluator func([]msp.Identity, []bool) bool
deserializer msp.IdentityDeserializer
}
// EvaluateSignedData takes a set of SignedData and evaluates whether
// 1) the signatures are valid over the related message
// 2) the signing identities satisfy the policy
func (p *policy) EvaluateSignedData(signatureSet []*protoutil.SignedData) error {
if p == nil {
return errors.New("no such policy")
}
ids := policies.SignatureSetToValidIdentities(signatureSet, p.deserializer)
return p.EvaluateIdentities(ids)
}
// EvaluateIdentities takes an array of identities and evaluates whether
// they satisfy the policy
func (p *policy) EvaluateIdentities(identities []msp.Identity) error {
if p == nil {
return fmt.Errorf("No such policy")
}
ok := p.evaluator(identities, make([]bool, len(identities)))
if !ok {
return errors.New("signature set did not satisfy policy")
}
return nil
}
func (p *policy) Convert() (*cb.SignaturePolicyEnvelope, error) {
if p.signaturePolicyEnvelope == nil {
return nil, errors.New("nil policy field")
}
return p.signaturePolicyEnvelope, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package chaincode
import (
"sync"
"github.com/hyperledger/fabric-protos-go-apiv2/gossip"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
)
// InstalledChaincode defines metadata about an installed chaincode
type InstalledChaincode struct {
PackageID string
Hash []byte
Label string
// References is a map of channel name to chaincode
// metadata. This represents the channels and chaincode
// definitions that use this installed chaincode package.
References map[string][]*Metadata
// FIXME: we should remove these two
// fields since they are not properties
// of the chaincode (FAB-14561)
Name string
Version string
}
// Metadata defines channel-scoped metadata of a chaincode
type Metadata struct {
Name string
Version string
Policy []byte
// CollectionPolicies will only be set for _lifecycle
// chaincodes and stores a map from collection name to
// that collection's endorsement policy if one exists.
CollectionPolicies map[string][]byte
Id []byte
CollectionsConfig *peer.CollectionConfigPackage
// These two fields (Approved, Installed) are only set for
// _lifecycle chaincodes. They are used to ensure service
// discovery doesn't publish a stale chaincode definition
// when the _lifecycle definition exists but has not yet
// been installed or approved by the peer's org.
Approved bool
Installed bool
}
// MetadataSet defines an aggregation of Metadata
type MetadataSet []Metadata
// AsChaincodes converts this MetadataSet to a slice of gossip.Chaincodes
func (ccs MetadataSet) AsChaincodes() []*gossip.Chaincode {
var res []*gossip.Chaincode
for _, cc := range ccs {
res = append(res, &gossip.Chaincode{
Name: cc.Name,
Version: cc.Version,
})
}
return res
}
// MetadataMapping defines a mapping from chaincode name to Metadata
type MetadataMapping struct {
sync.RWMutex
mdByName map[string]Metadata
}
// NewMetadataMapping creates a new metadata mapping
func NewMetadataMapping() *MetadataMapping {
return &MetadataMapping{
mdByName: make(map[string]Metadata),
}
}
// Lookup returns the Metadata that is associated with the given chaincode
func (m *MetadataMapping) Lookup(cc string) (Metadata, bool) {
m.RLock()
defer m.RUnlock()
md, exists := m.mdByName[cc]
return md, exists
}
// Update updates the chaincode metadata in the mapping
func (m *MetadataMapping) Update(ccMd Metadata) {
m.Lock()
defer m.Unlock()
m.mdByName[ccMd.Name] = ccMd
}
// Aggregate aggregates all Metadata to a MetadataSet
func (m *MetadataMapping) Aggregate() MetadataSet {
m.RLock()
defer m.RUnlock()
var set MetadataSet
for _, md := range m.mdByName {
set = append(set, md)
}
return set
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"math"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
mspa "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/hyperledger/fabric/common/policydsl"
"github.com/hyperledger/fabric/protoutil"
)
const (
BlockValidationPolicyKey = "BlockValidation"
)
// EncodeBFTBlockVerificationPolicy creates a block verification policy based on Byzantine Fault Tolerance (BFT).
// It takes a list of consenters (orderer nodes), constructs a BFT policy using their identities, and updates
// the orderer's configuration group with this new policy.
func EncodeBFTBlockVerificationPolicy(consenterProtos []*cb.Consenter, ordererGroup *cb.ConfigGroup) {
n := len(consenterProtos)
f := (n - 1) / 3
var identities []*mspa.MSPPrincipal
var pols []*cb.SignaturePolicy
for i, consenter := range consenterProtos {
pols = append(pols, &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_SignedBy{
SignedBy: int32(i),
},
})
identities = append(identities, &mspa.MSPPrincipal{
PrincipalClassification: mspa.MSPPrincipal_IDENTITY,
Principal: protoutil.MarshalOrPanic(&mspa.SerializedIdentity{Mspid: consenter.MspId, IdBytes: consenter.Identity}),
})
}
quorumSize := ComputeBFTQuorum(n, f)
sp := &cb.SignaturePolicyEnvelope{
Rule: policydsl.NOutOf(int32(quorumSize), pols),
Identities: identities,
}
ordererGroup.Policies[BlockValidationPolicyKey] = &cb.ConfigPolicy{
// Inherit modification policy
ModPolicy: ordererGroup.Policies[BlockValidationPolicyKey].ModPolicy,
Policy: &cb.Policy{
Type: int32(cb.Policy_SIGNATURE),
Value: protoutil.MarshalOrPanic(sp),
},
}
}
func ComputeBFTQuorum(totalNodes, faultyNodes int) int {
return int(math.Ceil(float64(totalNodes+faultyNodes+1) / 2))
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"fmt"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/pkg/errors"
)
// remap explores the policy tree depth first and remaps the "signed by"
// entries according to the remapping rules; a "signed by" rule requires
// a signature from a principal given its position in the array of principals;
// the idRemap map tells us how to remap these integers given that merging two
// policies implies deduplicating their principals
func remap(sp *cb.SignaturePolicy, idRemap map[int]int) *cb.SignaturePolicy {
switch t := sp.Type.(type) {
case *cb.SignaturePolicy_NOutOf_:
rules := []*cb.SignaturePolicy{}
for _, rule := range t.NOutOf.Rules {
// here we call remap again - we're doing a
// depth-first traversal of this policy tree
rules = append(rules, remap(rule, idRemap))
}
return &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_NOutOf_{
NOutOf: &cb.SignaturePolicy_NOutOf{
N: t.NOutOf.N,
Rules: rules,
},
},
}
case *cb.SignaturePolicy_SignedBy:
// here we do the actual remapping because we have
// the "signed by" rule, whose reference to the
// principal we need to remap
newID, in := idRemap[int(t.SignedBy)]
if !in {
panic("programming error")
}
return &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_SignedBy{
SignedBy: int32(newID),
},
}
default:
panic(fmt.Sprintf("invalid policy type %T", t))
}
}
// merge integrates the policy `that` into the
// policy `this`. The first argument is changed
// whereas the second isn't
func merge(this *cb.SignaturePolicyEnvelope, that *cb.SignaturePolicyEnvelope) {
// at first we build a map of principals in `this`
IDs := this.Identities
idMap := map[string]int{}
for i, id := range this.Identities {
str := id.PrincipalClassification.String() + string(id.Principal)
idMap[str] = i
}
// then we traverse each of the principals in `that`,
// deduplicate them against the ones in `this` and
// create remapping rules so that if `that` references
// a duplicate policy in this, the merged policy will
// ensure that the references in `that` point to the
// correct principal
idRemap := map[int]int{}
for i, id := range that.Identities {
str := id.PrincipalClassification.String() + string(id.Principal)
if j, in := idMap[str]; in {
idRemap[i] = j
} else {
idRemap[i] = len(IDs)
idMap[str] = len(IDs)
IDs = append(IDs, id)
}
}
this.Identities = IDs
newEntry := remap(that.Rule, idRemap)
existingRules := this.Rule.Type.(*cb.SignaturePolicy_NOutOf_).NOutOf.Rules
this.Rule.Type.(*cb.SignaturePolicy_NOutOf_).NOutOf.Rules = append(existingRules, newEntry)
}
// Convert implements the policies.Converter function to
// convert an implicit meta policy into a signature policy envelope.
func (p *ImplicitMetaPolicy) Convert() (*cb.SignaturePolicyEnvelope, error) {
converted := &cb.SignaturePolicyEnvelope{
Version: 0,
Rule: &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_NOutOf_{
NOutOf: &cb.SignaturePolicy_NOutOf{
N: int32(p.Threshold),
},
},
},
}
// the conversion approach for an implicit meta
// policy is to convert each of the subpolicies,
// merge it with the previous one and return the
// merged policy
for i, subPolicy := range p.SubPolicies {
convertibleSubpolicy, ok := subPolicy.(Converter)
if !ok {
return nil, errors.Errorf("subpolicy number %d type %T of policy %s is not convertible", i, subPolicy, p.SubPolicyName)
}
spe, err := convertibleSubpolicy.Convert()
if err != nil {
return nil, errors.WithMessagef(err, "failed to convert subpolicy number %d of policy %s", i, p.SubPolicyName)
}
merge(converted, spe)
}
return converted, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"bytes"
"fmt"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"go.uber.org/zap/zapcore"
"google.golang.org/protobuf/proto"
)
type ImplicitMetaPolicy struct {
Threshold int
SubPolicies []Policy
// Only used for logging
managers map[string]*ManagerImpl
SubPolicyName string
}
// NewImplicitMetaPolicy creates a new policy based on the policy bytes
func NewImplicitMetaPolicy(data []byte, managers map[string]*ManagerImpl) (*ImplicitMetaPolicy, error) {
definition := &cb.ImplicitMetaPolicy{}
if err := proto.Unmarshal(data, definition); err != nil {
return nil, fmt.Errorf("Error unmarshalling to ImplicitMetaPolicy: %s", err)
}
subPolicies := make([]Policy, len(managers))
i := 0
for _, manager := range managers {
subPolicies[i], _ = manager.GetPolicy(definition.SubPolicy)
i++
}
var threshold int
switch definition.Rule {
case cb.ImplicitMetaPolicy_ANY:
threshold = 1
case cb.ImplicitMetaPolicy_ALL:
threshold = len(subPolicies)
case cb.ImplicitMetaPolicy_MAJORITY:
threshold = len(subPolicies)/2 + 1
}
// In the special case that there are no policies, consider 0 to be a majority or any
if len(subPolicies) == 0 {
threshold = 0
}
return &ImplicitMetaPolicy{
SubPolicies: subPolicies,
Threshold: threshold,
managers: managers,
SubPolicyName: definition.SubPolicy,
}, nil
}
// EvaluateSignedData takes a set of SignedData and evaluates whether this set of signatures satisfies the policy
func (imp *ImplicitMetaPolicy) EvaluateSignedData(signatureSet []*protoutil.SignedData) error {
logger.Debugf("This is an implicit meta policy, it will trigger other policy evaluations, whose failures may be benign")
remaining := imp.Threshold
defer func() {
if remaining != 0 {
// This log message may be large and expensive to construct, so worth checking the log level
if logger.IsEnabledFor(zapcore.DebugLevel) {
var b bytes.Buffer
b.WriteString(fmt.Sprintf("Evaluation Failed: Only %d policies were satisfied, but needed %d of [ ", imp.Threshold-remaining, imp.Threshold))
for m := range imp.managers {
b.WriteString(m)
b.WriteString("/")
b.WriteString(imp.SubPolicyName)
b.WriteString(" ")
}
b.WriteString("]")
logger.Debug(b.String())
}
}
}()
for _, policy := range imp.SubPolicies {
if policy.EvaluateSignedData(signatureSet) == nil {
remaining--
if remaining == 0 {
return nil
}
}
}
if remaining == 0 {
return nil
}
return fmt.Errorf("implicit policy evaluation failed - %d sub-policies were satisfied, but this policy requires %d of the '%s' sub-policies to be satisfied", imp.Threshold-remaining, imp.Threshold, imp.SubPolicyName)
}
// EvaluateIdentities takes an array of identities and evaluates whether
// they satisfy the policy
func (imp *ImplicitMetaPolicy) EvaluateIdentities(identities []msp.Identity) error {
logger.Debugf("This is an implicit meta policy, it will trigger other policy evaluations, whose failures may be benign")
remaining := imp.Threshold
defer func() {
// This log message may be large and expensive to construct, so worth checking the log level
if remaining == 0 {
return
}
if !logger.IsEnabledFor(zapcore.DebugLevel) {
return
}
var b bytes.Buffer
b.WriteString(fmt.Sprintf("Evaluation Failed: Only %d policies were satisfied, but needed %d of [ ", imp.Threshold-remaining, imp.Threshold))
for m := range imp.managers {
b.WriteString(m)
b.WriteString("/")
b.WriteString(imp.SubPolicyName)
b.WriteString(" ")
}
b.WriteString("]")
logger.Debug(b.String())
}()
for _, policy := range imp.SubPolicies {
if policy.EvaluateIdentities(identities) == nil {
remaining--
if remaining == 0 {
return nil
}
}
}
if remaining == 0 {
return nil
}
return fmt.Errorf("implicit policy evaluation failed - %d sub-policies were satisfied, but this policy requires %d of the '%s' sub-policies to be satisfied", imp.Threshold-remaining, imp.Threshold, imp.SubPolicyName)
}
/*
Copyright IBM Corp. 2016 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/protoutil"
)
// ImplicitMetaPolicyWithSubPolicy creates an implicitmeta policy
func ImplicitMetaPolicyWithSubPolicy(subPolicyName string, rule cb.ImplicitMetaPolicy_Rule) *cb.ConfigPolicy {
return &cb.ConfigPolicy{
Policy: &cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
Rule: rule,
SubPolicy: subPolicyName,
}),
},
}
}
// TemplateImplicitMetaPolicyWithSubPolicy creates a policy at the specified path with the given policyName and subPolicyName
func TemplateImplicitMetaPolicyWithSubPolicy(path []string, policyName string, subPolicyName string, rule cb.ImplicitMetaPolicy_Rule) *cb.ConfigGroup {
root := protoutil.NewConfigGroup()
group := root
for _, element := range path {
group.Groups[element] = protoutil.NewConfigGroup()
group = group.Groups[element]
}
group.Policies[policyName] = ImplicitMetaPolicyWithSubPolicy(subPolicyName, rule)
return root
}
// TemplateImplicitMetaPolicy creates a policy at the specified path with the given policyName
// It utilizes the policyName for the subPolicyName as well, as this is the standard usage pattern
func TemplateImplicitMetaPolicy(path []string, policyName string, rule cb.ImplicitMetaPolicy_Rule) *cb.ConfigGroup {
return TemplateImplicitMetaPolicyWithSubPolicy(path, policyName, policyName, rule)
}
// TemplateImplicitMetaAnyPolicy returns TemplateImplicitMetaPolicy with cb.ImplicitMetaPolicy_ANY as the rule
func TemplateImplicitMetaAnyPolicy(path []string, policyName string) *cb.ConfigGroup {
return TemplateImplicitMetaPolicy(path, policyName, cb.ImplicitMetaPolicy_ANY)
}
// TemplateImplicitMetaAllPolicy returns TemplateImplicitMetaPolicy with cb.ImplicitMetaPolicy_ALL as the rule
func TemplateImplicitMetaAllPolicy(path []string, policyName string) *cb.ConfigGroup {
return TemplateImplicitMetaPolicy(path, policyName, cb.ImplicitMetaPolicy_ALL)
}
// TemplateImplicitMetaMajorityPolicy returns TemplateImplicitMetaPolicy with cb.ImplicitMetaPolicy_MAJORITY as the rule
func TemplateImplicitMetaMajorityPolicy(path []string, policyName string) *cb.ConfigGroup {
return TemplateImplicitMetaPolicy(path, policyName, cb.ImplicitMetaPolicy_MAJORITY)
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"strings"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/pkg/errors"
)
func ImplicitMetaFromString(input string) (*cb.ImplicitMetaPolicy, error) {
args := strings.Split(input, " ")
if len(args) != 2 {
return nil, errors.Errorf("expected two space separated tokens, but got %d", len(args))
}
res := &cb.ImplicitMetaPolicy{
SubPolicy: args[1],
}
switch args[0] {
case cb.ImplicitMetaPolicy_ANY.String():
res.Rule = cb.ImplicitMetaPolicy_ANY
case cb.ImplicitMetaPolicy_ALL.String():
res.Rule = cb.ImplicitMetaPolicy_ALL
case cb.ImplicitMetaPolicy_MAJORITY.String():
res.Rule = cb.ImplicitMetaPolicy_MAJORITY
default:
return nil, errors.Errorf("unknown rule type '%s', expected ALL, ANY, or MAJORITY", args[0])
}
return res, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
"fmt"
"strings"
"github.com/hyperledger/fabric-lib-go/common/flogging"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
mspi "github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
"google.golang.org/protobuf/proto"
)
const (
// Path separator is used to separate policy names in paths
PathSeparator = "/"
// ChannelPrefix is used in the path of standard channel policy managers
ChannelPrefix = "Channel"
// ApplicationPrefix is used in the path of standard application policy paths
ApplicationPrefix = "Application"
// OrdererPrefix is used in the path of standard orderer policy paths
OrdererPrefix = "Orderer"
// ChannelReaders is the label for the channel's readers policy (encompassing both orderer and application readers)
ChannelReaders = PathSeparator + ChannelPrefix + PathSeparator + "Readers"
// ChannelWriters is the label for the channel's writers policy (encompassing both orderer and application writers)
ChannelWriters = PathSeparator + ChannelPrefix + PathSeparator + "Writers"
// ChannelApplicationReaders is the label for the channel's application readers policy
ChannelApplicationReaders = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Readers"
// ChannelApplicationWriters is the label for the channel's application writers policy
ChannelApplicationWriters = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Writers"
// ChannelApplicationAdmins is the label for the channel's application admin policy
ChannelApplicationAdmins = PathSeparator + ChannelPrefix + PathSeparator + ApplicationPrefix + PathSeparator + "Admins"
// BlockValidation is the label for the policy which should validate the block signatures for the channel
BlockValidation = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "BlockValidation"
// ChannelOrdererAdmins is the label for the channel's orderer admin policy
ChannelOrdererAdmins = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "Admins"
// ChannelOrdererWriters is the label for the channel's orderer writers policy
ChannelOrdererWriters = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "Writers"
// ChannelOrdererReaders is the label for the channel's orderer readers policy
ChannelOrdererReaders = PathSeparator + ChannelPrefix + PathSeparator + OrdererPrefix + PathSeparator + "Readers"
)
var logger = flogging.MustGetLogger("policies")
// PrincipalSet is a collection of MSPPrincipals
type PrincipalSet []*msp.MSPPrincipal
// PrincipalSets aggregates PrincipalSets
type PrincipalSets []PrincipalSet
// ContainingOnly returns PrincipalSets that contain only principals of the given predicate
func (psSets PrincipalSets) ContainingOnly(f func(*msp.MSPPrincipal) bool) PrincipalSets {
var res PrincipalSets
for _, set := range psSets {
if !set.ContainingOnly(f) {
continue
}
res = append(res, set)
}
return res
}
// ContainingOnly returns whether the given PrincipalSet contains only Principals
// that satisfy the given predicate
func (ps PrincipalSet) ContainingOnly(f func(*msp.MSPPrincipal) bool) bool {
for _, principal := range ps {
if !f(principal) {
return false
}
}
return true
}
// UniqueSet returns a histogram that is induced by the PrincipalSet
func (ps PrincipalSet) UniqueSet() map[*msp.MSPPrincipal]int {
// Create a histogram that holds the MSPPrincipals and counts them
histogram := make(map[struct {
cls int32
principal string
}]int)
// Now, populate the histogram
for _, principal := range ps {
key := struct {
cls int32
principal string
}{
cls: int32(principal.PrincipalClassification),
principal: string(principal.Principal),
}
histogram[key]++
}
// Finally, convert to a histogram of MSPPrincipal pointers
res := make(map[*msp.MSPPrincipal]int)
for principal, count := range histogram {
res[&msp.MSPPrincipal{
PrincipalClassification: msp.MSPPrincipal_Classification(principal.cls),
Principal: []byte(principal.principal),
}] = count
}
return res
}
// Converter represents a policy
// which may be translated into a SignaturePolicyEnvelope
type Converter interface {
Convert() (*cb.SignaturePolicyEnvelope, error)
}
// Policy is used to determine if a signature is valid
type Policy interface {
// EvaluateSignedData takes a set of SignedData and evaluates whether
// 1) the signatures are valid over the related message
// 2) the signing identities satisfy the policy
EvaluateSignedData(signatureSet []*protoutil.SignedData) error
// EvaluateIdentities takes an array of identities and evaluates whether
// they satisfy the policy
EvaluateIdentities(identities []mspi.Identity) error
}
// InquireablePolicy is a Policy that one can inquire
type InquireablePolicy interface {
// SatisfiedBy returns a slice of PrincipalSets that each of them
// satisfies the policy.
SatisfiedBy() []PrincipalSet
}
// Manager is a read only subset of the policy ManagerImpl
type Manager interface {
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default policy
GetPolicy(id string) (Policy, bool)
// Manager returns the sub-policy manager for a given path and whether it exists
Manager(path []string) (Manager, bool)
}
// Provider provides the backing implementation of a policy
type Provider interface {
// NewPolicy creates a new policy based on the policy bytes
NewPolicy(data []byte) (Policy, proto.Message, error)
}
// ChannelPolicyManagerGetter is a support interface
// to get access to the policy manager of a given channel
type ChannelPolicyManagerGetter interface {
// Returns the policy manager associated with the specified channel.
Manager(channelID string) Manager
}
// PolicyManagerGetterFunc is a function adapter for ChannelPolicyManagerGetter.
type PolicyManagerGetterFunc func(channelID string) Manager
func (p PolicyManagerGetterFunc) Manager(channelID string) Manager { return p(channelID) }
// ManagerImpl is an implementation of Manager and configtx.ConfigHandler
// In general, it should only be referenced as an Impl for the configtx.ConfigManager
type ManagerImpl struct {
path string // The group level path
Policies map[string]Policy
managers map[string]*ManagerImpl
}
// NewManagerImpl creates a new ManagerImpl with the given CryptoHelper
func NewManagerImpl(path string, providers map[int32]Provider, root *cb.ConfigGroup) (*ManagerImpl, error) {
var err error
_, ok := providers[int32(cb.Policy_IMPLICIT_META)]
if ok {
logger.Panicf("ImplicitMetaPolicy type must be provider by the policy manager")
}
managers := make(map[string]*ManagerImpl)
for groupName, group := range root.Groups {
managers[groupName], err = NewManagerImpl(path+PathSeparator+groupName, providers, group)
if err != nil {
return nil, err
}
}
policies := make(map[string]Policy)
for policyName, configPolicy := range root.Policies {
policy := configPolicy.Policy
if policy == nil {
return nil, fmt.Errorf("policy %s at path %s was nil", policyName, path)
}
var cPolicy Policy
if policy.Type == int32(cb.Policy_IMPLICIT_META) {
imp, err := NewImplicitMetaPolicy(policy.Value, managers)
if err != nil {
return nil, errors.Wrapf(err, "implicit policy %s at path %s did not compile", policyName, path)
}
cPolicy = imp
} else {
provider, ok := providers[policy.Type]
if !ok {
return nil, fmt.Errorf("policy %s at path %s has unknown policy type: %v", policyName, path, policy.Type)
}
var err error
cPolicy, _, err = provider.NewPolicy(policy.Value)
if err != nil {
return nil, errors.Wrapf(err, "policy %s at path %s did not compile", policyName, path)
}
}
policies[policyName] = cPolicy
logger.Debugf("Proposed new policy %s for %s", policyName, path)
}
for groupName, manager := range managers {
for policyName, policy := range manager.Policies {
policies[groupName+PathSeparator+policyName] = policy
}
}
return &ManagerImpl{
path: path,
Policies: policies,
managers: managers,
}, nil
}
type rejectPolicy string
func (rp rejectPolicy) EvaluateSignedData(signedData []*protoutil.SignedData) error {
return errors.Errorf("no such policy: '%s'", rp)
}
func (rp rejectPolicy) EvaluateIdentities(identities []mspi.Identity) error {
return errors.Errorf("no such policy: '%s'", rp)
}
// Manager returns the sub-policy manager for a given path and whether it exists
func (pm *ManagerImpl) Manager(path []string) (Manager, bool) {
logger.Debugf("Manager %s looking up path %v", pm.path, path)
for manager := range pm.managers {
logger.Debugf("Manager %s has managers %s", pm.path, manager)
}
if len(path) == 0 {
return pm, true
}
m, ok := pm.managers[path[0]]
if !ok {
return nil, false
}
return m.Manager(path[1:])
}
type PolicyLogger struct {
Policy Policy
policyName string
}
func (pl *PolicyLogger) EvaluateSignedData(signatureSet []*protoutil.SignedData) error {
if logger.IsEnabledFor(zapcore.DebugLevel) {
logger.Debugf("== Evaluating %T Policy %s ==", pl.Policy, pl.policyName)
defer logger.Debugf("== Done Evaluating %T Policy %s", pl.Policy, pl.policyName)
}
err := pl.Policy.EvaluateSignedData(signatureSet)
if err != nil {
logger.Debugf("Signature set did not satisfy policy %s", pl.policyName)
} else {
logger.Debugf("Signature set satisfies policy %s", pl.policyName)
}
return err
}
func (pl *PolicyLogger) EvaluateIdentities(identities []mspi.Identity) error {
if logger.IsEnabledFor(zapcore.DebugLevel) {
logger.Debugf("== Evaluating %T Policy %s ==", pl.Policy, pl.policyName)
defer logger.Debugf("== Done Evaluating %T Policy %s", pl.Policy, pl.policyName)
}
err := pl.Policy.EvaluateIdentities(identities)
if err != nil {
logger.Debugf("Signature set did not satisfy policy %s", pl.policyName)
} else {
logger.Debugf("Signature set satisfies policy %s", pl.policyName)
}
return err
}
func (pl *PolicyLogger) Convert() (*cb.SignaturePolicyEnvelope, error) {
logger.Debugf("== Converting %T Policy %s ==", pl.Policy, pl.policyName)
convertiblePolicy, ok := pl.Policy.(Converter)
if !ok {
logger.Errorf("policy (name='%s',type='%T') is not convertible to SignaturePolicyEnvelope", pl.policyName, pl.Policy)
return nil, errors.Errorf("policy (name='%s',type='%T') is not convertible to SignaturePolicyEnvelope", pl.policyName, pl.Policy)
}
cp, err := convertiblePolicy.Convert()
if err != nil {
logger.Errorf("== Error Converting %T Policy %s, err %s", pl.Policy, pl.policyName, err.Error())
} else {
logger.Debugf("== Done Converting %T Policy %s", pl.Policy, pl.policyName)
}
return cp, err
}
// GetPolicy returns a policy and true if it was the policy requested, or false if it is the default reject policy
func (pm *ManagerImpl) GetPolicy(id string) (Policy, bool) {
if id == "" {
logger.Errorf("Returning dummy reject all policy because no policy ID supplied")
return rejectPolicy(id), false
}
var relpath string
if strings.HasPrefix(id, PathSeparator) {
if !strings.HasPrefix(id, PathSeparator+pm.path) {
logger.Debugf("Requested absolute policy %s from %s, returning rejectAll", id, pm.path)
return rejectPolicy(id), false
}
// strip off the leading slash, the path, and the trailing slash
relpath = id[1+len(pm.path)+1:]
} else {
relpath = id
}
policy, ok := pm.Policies[relpath]
if !ok {
logger.Debugf("Returning dummy reject all policy because %s could not be found in %s/%s", id, pm.path, relpath)
return rejectPolicy(relpath), false
}
return &PolicyLogger{
Policy: policy,
policyName: PathSeparator + pm.path + PathSeparator + relpath,
}, true
}
// SignatureSetToValidIdentities takes a slice of pointers to signed data,
// checks the validity of the signature and of the signer and returns a
// slice of associated identities. The returned identities are deduplicated.
func SignatureSetToValidIdentities(signedData []*protoutil.SignedData, identityDeserializer mspi.IdentityDeserializer) []mspi.Identity {
idMap := map[string]struct{}{}
identities := make([]mspi.Identity, 0, len(signedData))
for i, sd := range signedData {
identity, err := identityDeserializer.DeserializeIdentity(sd.Identity)
if err != nil {
logger.Warnw("invalid identity", "error", err.Error(), "identity", protoutil.LogMessageForSerializedIdentity(sd.Identity))
continue
}
key := identity.GetIdentifier().Mspid + identity.GetIdentifier().Id
// We check if this identity has already appeared before doing a signature check, to ensure that
// someone cannot force us to waste time checking the same signature thousands of times
if _, ok := idMap[key]; ok {
logger.Warningf("De-duplicating identity [%s] at index %d in signature set", key, i)
continue
}
err = identity.Verify(sd.Data, sd.Signature)
if err != nil {
logger.Warningf("signature for identity %d is invalid: %s", i, err)
continue
}
logger.Debugf("signature for identity %d validated", i)
idMap[key] = struct{}{}
identities = append(identities, identity)
}
return identities
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policies
import (
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/protoutil"
)
// ConfigPolicy defines a common representation for different *cb.ConfigPolicy values.
type ConfigPolicy interface {
// Key is the key this value should be stored in the *cb.ConfigGroup.Policies map.
Key() string
// Value is the backing policy implementation for this ConfigPolicy
Value() *cb.Policy
}
// StandardConfigPolicy implements the ConfigValue interface.
type StandardConfigPolicy struct {
key string
value *cb.Policy
}
// Key is the key this value should be stored in the *cb.ConfigGroup.Values map.
func (scv *StandardConfigPolicy) Key() string {
return scv.key
}
// Value is the *cb.Policy which should be stored as the *cb.ConfigPolicy.Policy.
func (scv *StandardConfigPolicy) Value() *cb.Policy {
return scv.value
}
func makeImplicitMetaPolicy(subPolicyName string, rule cb.ImplicitMetaPolicy_Rule) *cb.Policy {
return &cb.Policy{
Type: int32(cb.Policy_IMPLICIT_META),
Value: protoutil.MarshalOrPanic(&cb.ImplicitMetaPolicy{
Rule: rule,
SubPolicy: subPolicyName,
}),
}
}
// ImplicitMetaAllPolicy defines an implicit meta policy whose sub_policy and key is policyname with rule ALL.
func ImplicitMetaAllPolicy(policyName string) *StandardConfigPolicy {
return &StandardConfigPolicy{
key: policyName,
value: makeImplicitMetaPolicy(policyName, cb.ImplicitMetaPolicy_ALL),
}
}
// ImplicitMetaAnyPolicy defines an implicit meta policy whose sub_policy and key is policyname with rule ANY.
func ImplicitMetaAnyPolicy(policyName string) *StandardConfigPolicy {
return &StandardConfigPolicy{
key: policyName,
value: makeImplicitMetaPolicy(policyName, cb.ImplicitMetaPolicy_ANY),
}
}
// ImplicitMetaMajorityPolicy defines an implicit meta policy whose sub_policy and key is policyname with rule MAJORITY.
func ImplicitMetaMajorityPolicy(policyName string) *StandardConfigPolicy {
return &StandardConfigPolicy{
key: policyName,
value: makeImplicitMetaPolicy(policyName, cb.ImplicitMetaPolicy_MAJORITY),
}
}
// SignaturePolicy defines a policy with key policyName and the given signature policy.
func SignaturePolicy(policyName string, sigPolicy *cb.SignaturePolicyEnvelope) *StandardConfigPolicy {
return &StandardConfigPolicy{
key: policyName,
value: &cb.Policy{
Type: int32(cb.Policy_SIGNATURE),
Value: protoutil.MarshalOrPanic(sigPolicy),
},
}
}
/*
Copyright IBM Corp. 2016 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policydsl
import (
"sort"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
mb "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// AcceptAllPolicy always evaluates to true
var AcceptAllPolicy *cb.SignaturePolicyEnvelope
// MarshaledAcceptAllPolicy is the Marshaled version of AcceptAllPolicy
var MarshaledAcceptAllPolicy []byte
// RejectAllPolicy always evaluates to false
var RejectAllPolicy *cb.SignaturePolicyEnvelope
// MarshaledRejectAllPolicy is the Marshaled version of RejectAllPolicy
var MarshaledRejectAllPolicy []byte
func init() {
AcceptAllPolicy = Envelope(NOutOf(0, []*cb.SignaturePolicy{}), [][]byte{})
MarshaledAcceptAllPolicy = protoMarshalOrPanic(AcceptAllPolicy)
RejectAllPolicy = Envelope(NOutOf(1, []*cb.SignaturePolicy{}), [][]byte{})
MarshaledRejectAllPolicy = protoMarshalOrPanic(RejectAllPolicy)
}
// Envelope builds an envelope message embedding a SignaturePolicy
func Envelope(policy *cb.SignaturePolicy, identities [][]byte) *cb.SignaturePolicyEnvelope {
ids := make([]*mb.MSPPrincipal, len(identities))
for i := range ids {
ids[i] = &mb.MSPPrincipal{PrincipalClassification: mb.MSPPrincipal_IDENTITY, Principal: identities[i]}
}
return &cb.SignaturePolicyEnvelope{
Version: 0,
Rule: policy,
Identities: ids,
}
}
// SignedBy creates a SignaturePolicy requiring a given signer's signature
func SignedBy(index int32) *cb.SignaturePolicy {
return &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_SignedBy{
SignedBy: index,
},
}
}
// SignedByMspMember creates a SignaturePolicyEnvelope
// requiring 1 signature from any member of the specified MSP
func SignedByMspMember(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, mb.MSPRole_MEMBER)
}
// SignedByMspClient creates a SignaturePolicyEnvelope
// requiring 1 signature from any client of the specified MSP
func SignedByMspClient(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, mb.MSPRole_CLIENT)
}
// SignedByMspPeer creates a SignaturePolicyEnvelope
// requiring 1 signature from any peer of the specified MSP
func SignedByMspPeer(mspId string) *cb.SignaturePolicyEnvelope {
return signedByFabricEntity(mspId, mb.MSPRole_PEER)
}
// SignedByFabricEntity creates a SignaturePolicyEnvelope
// requiring 1 signature from any fabric entity, having the passed role, of the specified MSP
func signedByFabricEntity(mspId string, role mb.MSPRole_MSPRoleType) *cb.SignaturePolicyEnvelope {
// specify the principal: it's a member of the msp we just found
principal := &mb.MSPPrincipal{
PrincipalClassification: mb.MSPPrincipal_ROLE,
Principal: protoMarshalOrPanic(&mb.MSPRole{Role: role, MspIdentifier: mspId}),
}
// create the policy: it requires exactly 1 signature from the first (and only) principal
p := &cb.SignaturePolicyEnvelope{
Version: 0,
Rule: NOutOf(1, []*cb.SignaturePolicy{SignedBy(0)}),
Identities: []*mb.MSPPrincipal{principal},
}
return p
}
// SignedByMspAdmin creates a SignaturePolicyEnvelope
// requiring 1 signature from any admin of the specified MSP
func SignedByMspAdmin(mspId string) *cb.SignaturePolicyEnvelope {
// specify the principal: it's a member of the msp we just found
principal := &mb.MSPPrincipal{
PrincipalClassification: mb.MSPPrincipal_ROLE,
Principal: protoMarshalOrPanic(&mb.MSPRole{Role: mb.MSPRole_ADMIN, MspIdentifier: mspId}),
}
// create the policy: it requires exactly 1 signature from the first (and only) principal
p := &cb.SignaturePolicyEnvelope{
Version: 0,
Rule: NOutOf(1, []*cb.SignaturePolicy{SignedBy(0)}),
Identities: []*mb.MSPPrincipal{principal},
}
return p
}
// wrapper for generating "any of a given role" type policies
func signedByAnyOfGivenRole(role mb.MSPRole_MSPRoleType, ids []string) *cb.SignaturePolicyEnvelope {
return SignedByNOutOfGivenRole(1, role, ids)
}
func SignedByNOutOfGivenRole(n int32, role mb.MSPRole_MSPRoleType, ids []string) *cb.SignaturePolicyEnvelope {
// we create an array of principals, one principal
// per application MSP defined on this chain
sort.Strings(ids)
principals := make([]*mb.MSPPrincipal, len(ids))
sigspolicy := make([]*cb.SignaturePolicy, len(ids))
for i, id := range ids {
principals[i] = &mb.MSPPrincipal{
PrincipalClassification: mb.MSPPrincipal_ROLE,
Principal: protoMarshalOrPanic(&mb.MSPRole{Role: role, MspIdentifier: id}),
}
sigspolicy[i] = SignedBy(int32(i))
}
// create the policy: it requires exactly 1 signature from any of the principals
p := &cb.SignaturePolicyEnvelope{
Version: 0,
Rule: NOutOf(n, sigspolicy),
Identities: principals,
}
return p
}
// SignedByAnyMember returns a policy that requires one valid
// signature from a member of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyMember(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(mb.MSPRole_MEMBER, ids)
}
// SignedByAnyClient returns a policy that requires one valid
// signature from a client of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyClient(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(mb.MSPRole_CLIENT, ids)
}
// SignedByAnyPeer returns a policy that requires one valid
// signature from an orderer of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyPeer(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(mb.MSPRole_PEER, ids)
}
// SignedByAnyAdmin returns a policy that requires one valid
// signature from a admin of any of the orgs whose ids are
// listed in the supplied string array
func SignedByAnyAdmin(ids []string) *cb.SignaturePolicyEnvelope {
return signedByAnyOfGivenRole(mb.MSPRole_ADMIN, ids)
}
// And is a convenience method which utilizes NOutOf to produce And equivalent behavior
func And(lhs, rhs *cb.SignaturePolicy) *cb.SignaturePolicy {
return NOutOf(2, []*cb.SignaturePolicy{lhs, rhs})
}
// Or is a convenience method which utilizes NOutOf to produce Or equivalent behavior
func Or(lhs, rhs *cb.SignaturePolicy) *cb.SignaturePolicy {
return NOutOf(1, []*cb.SignaturePolicy{lhs, rhs})
}
// NOutOf creates a policy which requires N out of the slice of policies to evaluate to true
func NOutOf(n int32, policies []*cb.SignaturePolicy) *cb.SignaturePolicy {
return &cb.SignaturePolicy{
Type: &cb.SignaturePolicy_NOutOf_{
NOutOf: &cb.SignaturePolicy_NOutOf{
N: n,
Rules: policies,
},
},
}
}
// protoMarshalOrPanic serializes a protobuf message and panics if this
// operation fails
func protoMarshalOrPanic(pb proto.Message) []byte {
if !pb.ProtoReflect().IsValid() {
panic(errors.New("proto: Marshal called with nil"))
}
data, err := proto.Marshal(pb)
if err != nil {
panic(err)
}
return data
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package policydsl
func FuzzFromString(data []byte) int {
_, _ = FromString(string(data))
return 1
}
/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package policydsl
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/Knetic/govaluate"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
mb "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"google.golang.org/protobuf/proto"
)
// Gate values
const (
GateAnd = "And"
GateOr = "Or"
GateOutOf = "OutOf"
)
// Role values for principals
const (
RoleAdmin = "admin"
RoleMember = "member"
RoleClient = "client"
RolePeer = "peer"
RoleOrderer = "orderer"
)
var (
regex = regexp.MustCompile(
fmt.Sprintf("^([[:alnum:].-]+)([.])(%s|%s|%s|%s|%s)$",
RoleAdmin, RoleMember, RoleClient, RolePeer, RoleOrderer),
)
regexErr = regexp.MustCompile("^No parameter '([^']+)' found[.]$")
)
// a stub function - it returns the same string as it's passed.
// This will be evaluated by second/third passes to convert to a proto policy
func outof(args ...interface{}) (interface{}, error) {
toret := "outof("
if len(args) < 2 {
return nil, fmt.Errorf("expected at least two arguments to NOutOf. Given %d", len(args))
}
arg0 := args[0]
// govaluate treats all numbers as float64 only. But and/or may pass int/string. Allowing int/string for flexibility of caller
if n, ok := arg0.(float64); ok {
toret += strconv.Itoa(int(n))
} else if n, ok := arg0.(int); ok {
toret += strconv.Itoa(n)
} else if n, ok := arg0.(string); ok {
toret += n
} else {
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg0))
}
for _, arg := range args[1:] {
toret += ", "
switch t := arg.(type) {
case string:
if regex.MatchString(t) {
toret += "'" + t + "'"
} else {
toret += t
}
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
}
return toret + ")", nil
}
func and(args ...interface{}) (interface{}, error) {
args = append([]interface{}{len(args)}, args...)
return outof(args...)
}
func or(args ...interface{}) (interface{}, error) {
args = append([]interface{}{1}, args...)
return outof(args...)
}
// firstPass processes a variadic list of arguments and returns a formatted string.
// The function expects arguments to be of either string, float32, or float64 types.
func firstPass(args ...interface{}) (interface{}, error) {
toret := "outof(ID"
for _, arg := range args {
toret += ", "
switch t := arg.(type) {
case string:
if regex.MatchString(t) {
toret += "'" + t + "'"
} else {
toret += t
}
case float32:
case float64:
toret += strconv.Itoa(int(t))
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
}
return toret + ")", nil
}
// secondPass processes a list of arguments to build a "t-out-of-n" policy.
// It expects the first argument to be a context, the second an integer (threshold t),
// and the rest as either principals (strings) or pre-existing policies.
func secondPass(args ...interface{}) (interface{}, error) {
/* general sanity check, we expect at least 3 args */
if len(args) < 3 {
return nil, fmt.Errorf("at least 3 arguments expected, got %d", len(args))
}
/* get the first argument, we expect it to be the context */
var ctx *context
switch v := args[0].(type) {
case *context:
ctx = v
default:
return nil, fmt.Errorf("unrecognized type, expected the context, got %s", reflect.TypeOf(args[0]))
}
/* get the second argument, we expect an integer telling us
how many of the remaining we expect to have*/
var t int
switch arg := args[1].(type) {
case float64:
t = int(arg)
default:
return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1]))
}
/* get the n in the t out of n */
n := len(args) - 2
/* sanity check - t should be positive, permit equal to n+1, but disallow over n+1 */
if t < 0 || t > n+1 {
return nil, fmt.Errorf("invalid t-out-of-n predicate, t %d, n %d", t, n)
}
policies := make([]*cb.SignaturePolicy, 0)
/* handle the rest of the arguments */
for _, principal := range args[2:] {
switch t := principal.(type) {
/* if it's a string, we expect it to be formed as
<MSP_ID> . <ROLE>, where MSP_ID is the MSP identifier
and ROLE is either a member, an admin, a client, a peer or an orderer*/
case string:
/* split the string */
subm := regex.FindAllStringSubmatch(t, -1)
if subm == nil || len(subm) != 1 || len(subm[0]) != 4 {
return nil, fmt.Errorf("error parsing principal %s", t)
}
/* get the right role */
var r mb.MSPRole_MSPRoleType
switch subm[0][3] {
case RoleMember:
r = mb.MSPRole_MEMBER
case RoleAdmin:
r = mb.MSPRole_ADMIN
case RoleClient:
r = mb.MSPRole_CLIENT
case RolePeer:
r = mb.MSPRole_PEER
case RoleOrderer:
r = mb.MSPRole_ORDERER
default:
return nil, fmt.Errorf("error parsing role %s", t)
}
/* build the principal we've been told */
mspRole, err := proto.Marshal(&mb.MSPRole{MspIdentifier: subm[0][1], Role: r})
if err != nil {
return nil, fmt.Errorf("error marshalling msp role: %s", err)
}
p := &mb.MSPPrincipal{
PrincipalClassification: mb.MSPPrincipal_ROLE,
Principal: mspRole,
}
ctx.principals = append(ctx.principals, p)
/* create a SignaturePolicy that requires a signature from
the principal we've just built*/
dapolicy := SignedBy(int32(ctx.IDNum))
policies = append(policies, dapolicy)
/* increment the identity counter. Note that this is
suboptimal as we are not reusing identities. We
can deduplicate them easily and make this puppy
smaller. For now it's fine though */
// TODO: deduplicate principals
ctx.IDNum++
/* if we've already got a policy we're good, just append it */
case *cb.SignaturePolicy:
policies = append(policies, t)
default:
return nil, fmt.Errorf("unrecognized type, expected a principal or a policy, got %s", reflect.TypeOf(principal))
}
}
return NOutOf(int32(t), policies), nil
}
type context struct {
IDNum int
principals []*mb.MSPPrincipal
}
func newContext() *context {
return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)}
}
// FromString takes a string representation of the policy,
// parses it and returns a SignaturePolicyEnvelope that
// implements that policy. The supported language is as follows:
//
// GATE(P[, P])
//
// where:
// - GATE is either "and" or "or"
// - P is either a principal or another nested call to GATE
//
// A principal is defined as:
//
// # ORG.ROLE
//
// where:
// - ORG is a string (representing the MSP identifier)
// - ROLE takes the value of any of the RoleXXX constants representing
// the required role
func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
// first we translate the and/or business into outof gates
intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(
policy, map[string]govaluate.ExpressionFunction{
GateAnd: and,
strings.ToLower(GateAnd): and,
strings.ToUpper(GateAnd): and,
GateOr: or,
strings.ToLower(GateOr): or,
strings.ToUpper(GateOr): or,
GateOutOf: outof,
strings.ToLower(GateOutOf): outof,
strings.ToUpper(GateOutOf): outof,
},
)
if err != nil {
return nil, err
}
intermediateRes, err := intermediate.Evaluate(map[string]interface{}{})
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
resStr, ok := intermediateRes.(string)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
// we still need two passes. The first pass just adds an extra
// argument ID to each of the outof calls. This is
// required because govaluate has no means of giving context
// to user-implemented functions other than via arguments.
// We need this argument because we need a global place where
// we put the identities that the policy requires
exp, err := govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": firstPass},
)
if err != nil {
return nil, err
}
res, err := exp.Evaluate(map[string]interface{}{})
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
resStr, ok = res.(string)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
ctx := newContext()
parameters := make(map[string]interface{}, 1)
parameters["ID"] = ctx
exp, err = govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": secondPass},
)
if err != nil {
return nil, err
}
res, err = exp.Evaluate(parameters)
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
sm := regexErr.FindStringSubmatch(err.Error())
if len(sm) == 2 {
return nil, fmt.Errorf("unrecognized token '%s' in policy string", sm[1])
}
}
return nil, err
}
rule, ok := res.(*cb.SignaturePolicy)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
}
p := &cb.SignaturePolicyEnvelope{
Identities: ctx.principals,
Version: 0,
Rule: rule,
}
return p, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package util
import (
"context"
"crypto/sha256"
"crypto/x509"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
)
func ExtractRemoteAddress(ctx context.Context) string {
p, ok := peer.FromContext(ctx)
if !ok {
return ""
}
if address := p.Addr; address != nil {
return address.String()
}
return ""
}
// ExtractCertificateHashFromContext extracts the hash of the certificate from the given context.
// If the certificate isn't present, nil is returned
func ExtractCertificateHashFromContext(ctx context.Context) []byte {
rawCert := ExtractRawCertificateFromContext(ctx)
if len(rawCert) == 0 {
return nil
}
h := sha256.New()
h.Write(rawCert)
return h.Sum(nil)
}
// ExtractCertificateFromContext returns the TLS certificate (if applicable)
// from the given context of a gRPC stream
func ExtractCertificateFromContext(ctx context.Context) *x509.Certificate {
pr, extracted := peer.FromContext(ctx)
if !extracted {
return nil
}
authInfo := pr.AuthInfo
if authInfo == nil {
return nil
}
tlsInfo, isTLSConn := authInfo.(credentials.TLSInfo)
if !isTLSConn {
return nil
}
certs := tlsInfo.State.PeerCertificates
if len(certs) == 0 {
return nil
}
return certs[0]
}
// ExtractRawCertificateFromContext returns the raw TLS certificate (if applicable)
// from the given context of a gRPC stream
func ExtractRawCertificateFromContext(ctx context.Context) []byte {
cert := ExtractCertificateFromContext(ctx)
if cert == nil {
return nil
}
return cert.Raw
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package util
import (
"crypto/rand"
"fmt"
"io"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/factory"
"google.golang.org/protobuf/types/known/timestamppb"
)
// ComputeSHA256 returns SHA2-256 on data
func ComputeSHA256(data []byte) (hash []byte) {
hash, err := factory.GetDefault().Hash(data, &bccsp.SHA256Opts{})
if err != nil {
panic(fmt.Errorf("Failed computing SHA256 on [% x]", data))
}
return
}
// ComputeSHA3256 returns SHA3-256 on data
func ComputeSHA3256(data []byte) (hash []byte) {
hash, err := factory.GetDefault().Hash(data, &bccsp.SHA3_256Opts{})
if err != nil {
panic(fmt.Errorf("Failed computing SHA3_256 on [% x]", data))
}
return
}
// GenerateBytesUUID returns a UUID based on RFC 4122 returning the generated bytes
func GenerateBytesUUID() []byte {
uuid := make([]byte, 16)
_, err := io.ReadFull(rand.Reader, uuid)
if err != nil {
panic(fmt.Sprintf("Error generating UUID: %s", err))
}
// variant bits; see section 4.1.1
uuid[8] = uuid[8]&^0xc0 | 0x80
// version 4 (pseudo-random); see section 4.1.3
uuid[6] = uuid[6]&^0xf0 | 0x40
return uuid
}
// GenerateUUID returns a UUID based on RFC 4122
func GenerateUUID() string {
uuid := GenerateBytesUUID()
return idBytesToStr(uuid)
}
// CreateUtcTimestamp returns a google/protobuf/Timestamp in UTC
func CreateUtcTimestamp() *timestamppb.Timestamp {
now := time.Now().UTC()
return timestamppb.New(now)
}
func idBytesToStr(id []byte) string {
return fmt.Sprintf("%x-%x-%x-%x-%x", id[0:4], id[4:6], id[6:8], id[8:10], id[10:])
}
// ToChaincodeArgs converts string args to []byte args
func ToChaincodeArgs(args ...string) [][]byte {
bargs := make([][]byte, len(args))
for i, arg := range args {
bargs[i] = []byte(arg)
}
return bargs
}
// ConcatenateBytes is useful for combining multiple arrays of bytes, especially for
// signatures or digests over multiple fields
// This way is more efficient in speed
func ConcatenateBytes(data ...[]byte) []byte {
finalLength := 0
for _, slice := range data {
finalLength += len(slice)
}
result := make([]byte, finalLength)
last := 0
for _, slice := range data {
last += copy(result[last:], slice)
}
return result
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package implicitcollection
import (
"strings"
)
const (
prefix = "_implicit_org_"
)
// NameForOrg constructs the name of the implicit collection for the specified org
func NameForOrg(mspid string) string {
return prefix + mspid
}
// MspIDIfImplicitCollection returns <true, mspid> if the specified name is a valid implicit collection name
// else it returns <false, "">
func MspIDIfImplicitCollection(collectionName string) (isImplicitCollection bool, mspid string) {
if !strings.HasPrefix(collectionName, prefix) {
return false, ""
}
return true, collectionName[len(prefix):]
}
func IsImplicitCollection(collectionName string) bool {
return strings.HasPrefix(collectionName, prefix)
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package persistence
import (
"archive/tar"
"bytes"
"compress/gzip"
"encoding/json"
"io"
"os"
"path/filepath"
"regexp"
pb "github.com/hyperledger/fabric-protos-go-apiv2/peer"
io2 "github.com/AdamKorcz/bugdetectors/io"
"github.com/pkg/errors"
)
// The chaincode package is simply a .tar.gz file. For the time being, we
// assume that the package contains a metadata.json file which contains a
// 'type', a 'path', and a 'label'. In the future, it would be nice if we
// move to a more buildpack type system, rather than the below presented
// JAR+manifest type system, but for expediency and incremental changes,
// moving to a tar format over the proto format for a user-inspectable
// artifact seems like a good step.
const (
// MetadataFile is the expected location of the metadata json document
// in the top level of the chaincode package.
MetadataFile = "metadata.json"
// CodePackageFile is the expected location of the code package in the
// top level of the chaincode package
CodePackageFile = "code.tar.gz"
)
//go:generate counterfeiter -o mock/legacy_cc_package_locator.go --fake-name LegacyCCPackageLocator . LegacyCCPackageLocator
type LegacyCCPackageLocator interface {
GetChaincodeDepSpec(nameVersion string) (*pb.ChaincodeDeploymentSpec, error)
}
type FallbackPackageLocator struct {
ChaincodePackageLocator *ChaincodePackageLocator
LegacyCCPackageLocator LegacyCCPackageLocator
}
func (fpl *FallbackPackageLocator) GetChaincodePackage(packageID string) (*ChaincodePackageMetadata, []byte, io.ReadCloser, error) {
// XXX, this path has too many return parameters. We could split it into two calls,
// or, we could deserialize the metadata where it's needed. But, as written was the
// fastest path to fixing a bug around the mutation of metadata.
streamer := fpl.ChaincodePackageLocator.ChaincodePackageStreamer(packageID)
if streamer.Exists() {
metadata, err := streamer.Metadata()
if err != nil {
return nil, nil, nil, errors.WithMessagef(err, "error retrieving chaincode package metadata '%s'", packageID)
}
mdBytes, err := streamer.MetadataBytes()
if err != nil {
return nil, nil, nil, errors.WithMessagef(err, "error retrieving chaincode package metadata bytes '%s'", packageID)
}
tarStream, err := streamer.Code()
if err != nil {
return nil, nil, nil, errors.WithMessagef(err, "error retrieving chaincode package code '%s'", packageID)
}
return metadata, mdBytes, tarStream, nil
}
cds, err := fpl.LegacyCCPackageLocator.GetChaincodeDepSpec(packageID)
if err != nil {
return nil, nil, nil, errors.WithMessagef(err, "could not get legacy chaincode package '%s'", packageID)
}
md := &ChaincodePackageMetadata{
Path: cds.ChaincodeSpec.ChaincodeId.Path,
Type: cds.ChaincodeSpec.Type.String(),
Label: cds.ChaincodeSpec.ChaincodeId.Name,
}
mdBytes, err := json.Marshal(md)
if err != nil {
return nil, nil, nil, errors.WithMessagef(err, "could not marshal metadata for chaincode package '%s'", packageID)
}
return md,
mdBytes,
io.NopCloser(bytes.NewBuffer(cds.CodePackage)),
nil
}
type ChaincodePackageLocator struct {
ChaincodeDir string
}
func (cpl *ChaincodePackageLocator) ChaincodePackageStreamer(packageID string) *ChaincodePackageStreamer {
return &ChaincodePackageStreamer{
PackagePath: filepath.Join(cpl.ChaincodeDir, CCFileName(packageID)),
}
}
type ChaincodePackageStreamer struct {
PackagePath string
}
func (cps *ChaincodePackageStreamer) Exists() bool {
_, err := os.Stat(cps.PackagePath)
return err == nil
}
func (cps *ChaincodePackageStreamer) Metadata() (*ChaincodePackageMetadata, error) {
tarFileStream, err := cps.File(MetadataFile)
if err != nil {
return nil, errors.WithMessage(err, "could not get metadata file")
}
defer tarFileStream.Close()
metadata := &ChaincodePackageMetadata{}
err = json.NewDecoder(tarFileStream).Decode(metadata)
if err != nil {
return nil, errors.WithMessage(err, "could not parse metadata file")
}
return metadata, nil
}
func (cps *ChaincodePackageStreamer) MetadataBytes() ([]byte, error) {
tarFileStream, err := cps.File(MetadataFile)
if err != nil {
return nil, errors.WithMessage(err, "could not get metadata file")
}
defer tarFileStream.Close()
md, err := io2.ReadAll(tarFileStream, "/src/fabric/core/chaincode/persistence/chaincode_package.go:143:13 (May be slightly inaccurate) NEW_LINEio.ReadAll", true)
if err != nil {
return nil, errors.WithMessage(err, "could read metadata file")
}
return md, nil
}
func (cps *ChaincodePackageStreamer) Code() (*TarFileStream, error) {
tarFileStream, err := cps.File(CodePackageFile)
if err != nil {
return nil, errors.WithMessage(err, "could not get code package")
}
return tarFileStream, nil
}
func (cps *ChaincodePackageStreamer) File(name string) (tarFileStream *TarFileStream, err error) {
file, err := os.Open(cps.PackagePath)
if err != nil {
return nil, errors.WithMessagef(err, "could not open chaincode package at '%s'", cps.PackagePath)
}
defer func() {
if err != nil {
file.Close()
}
}()
gzReader, err := gzip.NewReader(file)
if err != nil {
return nil, errors.Wrapf(err, "error reading as gzip stream")
}
tarReader := tar.NewReader(gzReader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, errors.Wrapf(err, "error inspecting next tar header")
}
if header.Name != name {
continue
}
if header.Typeflag != tar.TypeReg {
return nil, errors.Errorf("tar entry %s is not a regular file, type %v", header.Name, header.Typeflag)
}
return &TarFileStream{
TarFile: tarReader,
FileStream: file,
}, nil
}
return nil, errors.Errorf("did not find file '%s' in package", name)
}
type TarFileStream struct {
TarFile io.Reader
FileStream io.Closer
}
func (tfs *TarFileStream) Read(p []byte) (int, error) {
return tfs.TarFile.Read(p)
}
func (tfs *TarFileStream) Close() error {
return tfs.FileStream.Close()
}
// ChaincodePackage represents the un-tar-ed format of the chaincode package.
type ChaincodePackage struct {
Metadata *ChaincodePackageMetadata
CodePackage []byte
DBArtifacts []byte
}
// ChaincodePackageMetadata contains the information necessary to understand
// the embedded code package.
type ChaincodePackageMetadata struct {
Type string `json:"type"`
Path string `json:"path"`
Label string `json:"label"`
}
// MetadataProvider provides the means to retrieve metadata
// information (for instance the DB indexes) from a code package.
type MetadataProvider interface {
GetDBArtifacts(codePackage []byte) ([]byte, error)
}
// ChaincodePackageParser provides the ability to parse chaincode packages.
type ChaincodePackageParser struct {
MetadataProvider MetadataProvider
}
// LabelRegexp is the regular expression controlling the allowed characters
// for the package label.
var LabelRegexp = regexp.MustCompile(`^[[:alnum:]][[:alnum:]_.+-]*$`)
// ValidateLabel return an error if the provided label contains any invalid
// characters, as determined by LabelRegexp.
func ValidateLabel(label string) error {
if !LabelRegexp.MatchString(label) {
return errors.Errorf("invalid label '%s'. Label must be non-empty, can only consist of alphanumerics, symbols from '.+-_', and can only begin with alphanumerics", label)
}
return nil
}
// Parse parses a set of bytes as a chaincode package
// and returns the parsed package as a struct
func (ccpp ChaincodePackageParser) Parse(source []byte) (*ChaincodePackage, error) {
ccPackageMetadata, codePackage, err := ParseChaincodePackage(source)
if err != nil {
return nil, err
}
dbArtifacts, err := ccpp.MetadataProvider.GetDBArtifacts(codePackage)
if err != nil {
return nil, errors.WithMessage(err, "error retrieving DB artifacts from code package")
}
return &ChaincodePackage{
Metadata: ccPackageMetadata,
CodePackage: codePackage,
DBArtifacts: dbArtifacts,
}, nil
}
// ParseChaincodePackage parses a set of bytes as a chaincode package
// and returns the parsed package as a metadata struct and a code package
func ParseChaincodePackage(source []byte) (*ChaincodePackageMetadata, []byte, error) {
gzReader, err := gzip.NewReader(bytes.NewBuffer(source))
if err != nil {
return &ChaincodePackageMetadata{}, nil, errors.Wrapf(err, "error reading as gzip stream")
}
tarReader := tar.NewReader(gzReader)
var codePackage []byte
var ccPackageMetadata *ChaincodePackageMetadata
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return ccPackageMetadata, nil, errors.Wrapf(err, "error inspecting next tar header")
}
if header.Typeflag != tar.TypeReg {
return ccPackageMetadata, nil, errors.Errorf("tar entry %s is not a regular file, type %v", header.Name, header.Typeflag)
}
fileBytes, err := io2.ReadAll(tarReader, "/src/fabric/core/chaincode/persistence/chaincode_package.go:305:21 (May be slightly inaccurate) NEW_LINEio.ReadAll", true)
if err != nil {
return ccPackageMetadata, nil, errors.Wrapf(err, "could not read %s from tar", header.Name)
}
switch header.Name {
case MetadataFile:
ccPackageMetadata = &ChaincodePackageMetadata{}
err := json.Unmarshal(fileBytes, ccPackageMetadata)
if err != nil {
return ccPackageMetadata, nil, errors.Wrapf(err, "could not unmarshal %s as json", MetadataFile)
}
case CodePackageFile:
codePackage = fileBytes
default:
logger.Warningf("Encountered unexpected file '%s' in top level of chaincode package", header.Name)
}
}
if codePackage == nil {
return ccPackageMetadata, nil, errors.Errorf("did not find a code package inside the package")
}
if ccPackageMetadata == nil {
return ccPackageMetadata, nil, errors.Errorf("did not find any package metadata (missing %s)", MetadataFile)
}
if err := ValidateLabel(ccPackageMetadata.Label); err != nil {
return ccPackageMetadata, nil, err
}
return ccPackageMetadata, codePackage, nil
}
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"os"
"sync"
)
type IOReadWriter struct {
ExistsStub func(string) (bool, error)
existsMutex sync.RWMutex
existsArgsForCall []struct {
arg1 string
}
existsReturns struct {
result1 bool
result2 error
}
existsReturnsOnCall map[int]struct {
result1 bool
result2 error
}
MakeDirStub func(string, os.FileMode) error
makeDirMutex sync.RWMutex
makeDirArgsForCall []struct {
arg1 string
arg2 os.FileMode
}
makeDirReturns struct {
result1 error
}
makeDirReturnsOnCall map[int]struct {
result1 error
}
ReadDirStub func(string) ([]os.FileInfo, error)
readDirMutex sync.RWMutex
readDirArgsForCall []struct {
arg1 string
}
readDirReturns struct {
result1 []os.FileInfo
result2 error
}
readDirReturnsOnCall map[int]struct {
result1 []os.FileInfo
result2 error
}
ReadFileStub func(string) ([]byte, error)
readFileMutex sync.RWMutex
readFileArgsForCall []struct {
arg1 string
}
readFileReturns struct {
result1 []byte
result2 error
}
readFileReturnsOnCall map[int]struct {
result1 []byte
result2 error
}
RemoveStub func(string) error
removeMutex sync.RWMutex
removeArgsForCall []struct {
arg1 string
}
removeReturns struct {
result1 error
}
removeReturnsOnCall map[int]struct {
result1 error
}
WriteFileStub func(string, string, []byte) error
writeFileMutex sync.RWMutex
writeFileArgsForCall []struct {
arg1 string
arg2 string
arg3 []byte
}
writeFileReturns struct {
result1 error
}
writeFileReturnsOnCall map[int]struct {
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *IOReadWriter) Exists(arg1 string) (bool, error) {
fake.existsMutex.Lock()
ret, specificReturn := fake.existsReturnsOnCall[len(fake.existsArgsForCall)]
fake.existsArgsForCall = append(fake.existsArgsForCall, struct {
arg1 string
}{arg1})
fake.recordInvocation("Exists", []interface{}{arg1})
fake.existsMutex.Unlock()
if fake.ExistsStub != nil {
return fake.ExistsStub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
fakeReturns := fake.existsReturns
return fakeReturns.result1, fakeReturns.result2
}
func (fake *IOReadWriter) ExistsCallCount() int {
fake.existsMutex.RLock()
defer fake.existsMutex.RUnlock()
return len(fake.existsArgsForCall)
}
func (fake *IOReadWriter) ExistsCalls(stub func(string) (bool, error)) {
fake.existsMutex.Lock()
defer fake.existsMutex.Unlock()
fake.ExistsStub = stub
}
func (fake *IOReadWriter) ExistsArgsForCall(i int) string {
fake.existsMutex.RLock()
defer fake.existsMutex.RUnlock()
argsForCall := fake.existsArgsForCall[i]
return argsForCall.arg1
}
func (fake *IOReadWriter) ExistsReturns(result1 bool, result2 error) {
fake.existsMutex.Lock()
defer fake.existsMutex.Unlock()
fake.ExistsStub = nil
fake.existsReturns = struct {
result1 bool
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) ExistsReturnsOnCall(i int, result1 bool, result2 error) {
fake.existsMutex.Lock()
defer fake.existsMutex.Unlock()
fake.ExistsStub = nil
if fake.existsReturnsOnCall == nil {
fake.existsReturnsOnCall = make(map[int]struct {
result1 bool
result2 error
})
}
fake.existsReturnsOnCall[i] = struct {
result1 bool
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) MakeDir(arg1 string, arg2 os.FileMode) error {
fake.makeDirMutex.Lock()
ret, specificReturn := fake.makeDirReturnsOnCall[len(fake.makeDirArgsForCall)]
fake.makeDirArgsForCall = append(fake.makeDirArgsForCall, struct {
arg1 string
arg2 os.FileMode
}{arg1, arg2})
fake.recordInvocation("MakeDir", []interface{}{arg1, arg2})
fake.makeDirMutex.Unlock()
if fake.MakeDirStub != nil {
return fake.MakeDirStub(arg1, arg2)
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.makeDirReturns
return fakeReturns.result1
}
func (fake *IOReadWriter) MakeDirCallCount() int {
fake.makeDirMutex.RLock()
defer fake.makeDirMutex.RUnlock()
return len(fake.makeDirArgsForCall)
}
func (fake *IOReadWriter) MakeDirCalls(stub func(string, os.FileMode) error) {
fake.makeDirMutex.Lock()
defer fake.makeDirMutex.Unlock()
fake.MakeDirStub = stub
}
func (fake *IOReadWriter) MakeDirArgsForCall(i int) (string, os.FileMode) {
fake.makeDirMutex.RLock()
defer fake.makeDirMutex.RUnlock()
argsForCall := fake.makeDirArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *IOReadWriter) MakeDirReturns(result1 error) {
fake.makeDirMutex.Lock()
defer fake.makeDirMutex.Unlock()
fake.MakeDirStub = nil
fake.makeDirReturns = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) MakeDirReturnsOnCall(i int, result1 error) {
fake.makeDirMutex.Lock()
defer fake.makeDirMutex.Unlock()
fake.MakeDirStub = nil
if fake.makeDirReturnsOnCall == nil {
fake.makeDirReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.makeDirReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) ReadDir(arg1 string) ([]os.FileInfo, error) {
fake.readDirMutex.Lock()
ret, specificReturn := fake.readDirReturnsOnCall[len(fake.readDirArgsForCall)]
fake.readDirArgsForCall = append(fake.readDirArgsForCall, struct {
arg1 string
}{arg1})
fake.recordInvocation("ReadDir", []interface{}{arg1})
fake.readDirMutex.Unlock()
if fake.ReadDirStub != nil {
return fake.ReadDirStub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
fakeReturns := fake.readDirReturns
return fakeReturns.result1, fakeReturns.result2
}
func (fake *IOReadWriter) ReadDirCallCount() int {
fake.readDirMutex.RLock()
defer fake.readDirMutex.RUnlock()
return len(fake.readDirArgsForCall)
}
func (fake *IOReadWriter) ReadDirCalls(stub func(string) ([]os.FileInfo, error)) {
fake.readDirMutex.Lock()
defer fake.readDirMutex.Unlock()
fake.ReadDirStub = stub
}
func (fake *IOReadWriter) ReadDirArgsForCall(i int) string {
fake.readDirMutex.RLock()
defer fake.readDirMutex.RUnlock()
argsForCall := fake.readDirArgsForCall[i]
return argsForCall.arg1
}
func (fake *IOReadWriter) ReadDirReturns(result1 []os.FileInfo, result2 error) {
fake.readDirMutex.Lock()
defer fake.readDirMutex.Unlock()
fake.ReadDirStub = nil
fake.readDirReturns = struct {
result1 []os.FileInfo
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) ReadDirReturnsOnCall(i int, result1 []os.FileInfo, result2 error) {
fake.readDirMutex.Lock()
defer fake.readDirMutex.Unlock()
fake.ReadDirStub = nil
if fake.readDirReturnsOnCall == nil {
fake.readDirReturnsOnCall = make(map[int]struct {
result1 []os.FileInfo
result2 error
})
}
fake.readDirReturnsOnCall[i] = struct {
result1 []os.FileInfo
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) ReadFile(arg1 string) ([]byte, error) {
fake.readFileMutex.Lock()
ret, specificReturn := fake.readFileReturnsOnCall[len(fake.readFileArgsForCall)]
fake.readFileArgsForCall = append(fake.readFileArgsForCall, struct {
arg1 string
}{arg1})
fake.recordInvocation("ReadFile", []interface{}{arg1})
fake.readFileMutex.Unlock()
if fake.ReadFileStub != nil {
return fake.ReadFileStub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
fakeReturns := fake.readFileReturns
return fakeReturns.result1, fakeReturns.result2
}
func (fake *IOReadWriter) ReadFileCallCount() int {
fake.readFileMutex.RLock()
defer fake.readFileMutex.RUnlock()
return len(fake.readFileArgsForCall)
}
func (fake *IOReadWriter) ReadFileCalls(stub func(string) ([]byte, error)) {
fake.readFileMutex.Lock()
defer fake.readFileMutex.Unlock()
fake.ReadFileStub = stub
}
func (fake *IOReadWriter) ReadFileArgsForCall(i int) string {
fake.readFileMutex.RLock()
defer fake.readFileMutex.RUnlock()
argsForCall := fake.readFileArgsForCall[i]
return argsForCall.arg1
}
func (fake *IOReadWriter) ReadFileReturns(result1 []byte, result2 error) {
fake.readFileMutex.Lock()
defer fake.readFileMutex.Unlock()
fake.ReadFileStub = nil
fake.readFileReturns = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) ReadFileReturnsOnCall(i int, result1 []byte, result2 error) {
fake.readFileMutex.Lock()
defer fake.readFileMutex.Unlock()
fake.ReadFileStub = nil
if fake.readFileReturnsOnCall == nil {
fake.readFileReturnsOnCall = make(map[int]struct {
result1 []byte
result2 error
})
}
fake.readFileReturnsOnCall[i] = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *IOReadWriter) Remove(arg1 string) error {
fake.removeMutex.Lock()
ret, specificReturn := fake.removeReturnsOnCall[len(fake.removeArgsForCall)]
fake.removeArgsForCall = append(fake.removeArgsForCall, struct {
arg1 string
}{arg1})
fake.recordInvocation("Remove", []interface{}{arg1})
fake.removeMutex.Unlock()
if fake.RemoveStub != nil {
return fake.RemoveStub(arg1)
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.removeReturns
return fakeReturns.result1
}
func (fake *IOReadWriter) RemoveCallCount() int {
fake.removeMutex.RLock()
defer fake.removeMutex.RUnlock()
return len(fake.removeArgsForCall)
}
func (fake *IOReadWriter) RemoveCalls(stub func(string) error) {
fake.removeMutex.Lock()
defer fake.removeMutex.Unlock()
fake.RemoveStub = stub
}
func (fake *IOReadWriter) RemoveArgsForCall(i int) string {
fake.removeMutex.RLock()
defer fake.removeMutex.RUnlock()
argsForCall := fake.removeArgsForCall[i]
return argsForCall.arg1
}
func (fake *IOReadWriter) RemoveReturns(result1 error) {
fake.removeMutex.Lock()
defer fake.removeMutex.Unlock()
fake.RemoveStub = nil
fake.removeReturns = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) RemoveReturnsOnCall(i int, result1 error) {
fake.removeMutex.Lock()
defer fake.removeMutex.Unlock()
fake.RemoveStub = nil
if fake.removeReturnsOnCall == nil {
fake.removeReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.removeReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) WriteFile(arg1 string, arg2 string, arg3 []byte) error {
var arg3Copy []byte
if arg3 != nil {
arg3Copy = make([]byte, len(arg3))
copy(arg3Copy, arg3)
}
fake.writeFileMutex.Lock()
ret, specificReturn := fake.writeFileReturnsOnCall[len(fake.writeFileArgsForCall)]
fake.writeFileArgsForCall = append(fake.writeFileArgsForCall, struct {
arg1 string
arg2 string
arg3 []byte
}{arg1, arg2, arg3Copy})
fake.recordInvocation("WriteFile", []interface{}{arg1, arg2, arg3Copy})
fake.writeFileMutex.Unlock()
if fake.WriteFileStub != nil {
return fake.WriteFileStub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.writeFileReturns
return fakeReturns.result1
}
func (fake *IOReadWriter) WriteFileCallCount() int {
fake.writeFileMutex.RLock()
defer fake.writeFileMutex.RUnlock()
return len(fake.writeFileArgsForCall)
}
func (fake *IOReadWriter) WriteFileCalls(stub func(string, string, []byte) error) {
fake.writeFileMutex.Lock()
defer fake.writeFileMutex.Unlock()
fake.WriteFileStub = stub
}
func (fake *IOReadWriter) WriteFileArgsForCall(i int) (string, string, []byte) {
fake.writeFileMutex.RLock()
defer fake.writeFileMutex.RUnlock()
argsForCall := fake.writeFileArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *IOReadWriter) WriteFileReturns(result1 error) {
fake.writeFileMutex.Lock()
defer fake.writeFileMutex.Unlock()
fake.WriteFileStub = nil
fake.writeFileReturns = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) WriteFileReturnsOnCall(i int, result1 error) {
fake.writeFileMutex.Lock()
defer fake.writeFileMutex.Unlock()
fake.WriteFileStub = nil
if fake.writeFileReturnsOnCall == nil {
fake.writeFileReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.writeFileReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *IOReadWriter) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.existsMutex.RLock()
defer fake.existsMutex.RUnlock()
fake.makeDirMutex.RLock()
defer fake.makeDirMutex.RUnlock()
fake.readDirMutex.RLock()
defer fake.readDirMutex.RUnlock()
fake.readFileMutex.RLock()
defer fake.readFileMutex.RUnlock()
fake.removeMutex.RLock()
defer fake.removeMutex.RUnlock()
fake.writeFileMutex.RLock()
defer fake.writeFileMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *IOReadWriter) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"sync"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/core/chaincode/persistence"
)
type LegacyCCPackageLocator struct {
GetChaincodeDepSpecStub func(string) (*peer.ChaincodeDeploymentSpec, error)
getChaincodeDepSpecMutex sync.RWMutex
getChaincodeDepSpecArgsForCall []struct {
arg1 string
}
getChaincodeDepSpecReturns struct {
result1 *peer.ChaincodeDeploymentSpec
result2 error
}
getChaincodeDepSpecReturnsOnCall map[int]struct {
result1 *peer.ChaincodeDeploymentSpec
result2 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpec(arg1 string) (*peer.ChaincodeDeploymentSpec, error) {
fake.getChaincodeDepSpecMutex.Lock()
ret, specificReturn := fake.getChaincodeDepSpecReturnsOnCall[len(fake.getChaincodeDepSpecArgsForCall)]
fake.getChaincodeDepSpecArgsForCall = append(fake.getChaincodeDepSpecArgsForCall, struct {
arg1 string
}{arg1})
fake.recordInvocation("GetChaincodeDepSpec", []interface{}{arg1})
fake.getChaincodeDepSpecMutex.Unlock()
if fake.GetChaincodeDepSpecStub != nil {
return fake.GetChaincodeDepSpecStub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
fakeReturns := fake.getChaincodeDepSpecReturns
return fakeReturns.result1, fakeReturns.result2
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpecCallCount() int {
fake.getChaincodeDepSpecMutex.RLock()
defer fake.getChaincodeDepSpecMutex.RUnlock()
return len(fake.getChaincodeDepSpecArgsForCall)
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpecCalls(stub func(string) (*peer.ChaincodeDeploymentSpec, error)) {
fake.getChaincodeDepSpecMutex.Lock()
defer fake.getChaincodeDepSpecMutex.Unlock()
fake.GetChaincodeDepSpecStub = stub
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpecArgsForCall(i int) string {
fake.getChaincodeDepSpecMutex.RLock()
defer fake.getChaincodeDepSpecMutex.RUnlock()
argsForCall := fake.getChaincodeDepSpecArgsForCall[i]
return argsForCall.arg1
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpecReturns(result1 *peer.ChaincodeDeploymentSpec, result2 error) {
fake.getChaincodeDepSpecMutex.Lock()
defer fake.getChaincodeDepSpecMutex.Unlock()
fake.GetChaincodeDepSpecStub = nil
fake.getChaincodeDepSpecReturns = struct {
result1 *peer.ChaincodeDeploymentSpec
result2 error
}{result1, result2}
}
func (fake *LegacyCCPackageLocator) GetChaincodeDepSpecReturnsOnCall(i int, result1 *peer.ChaincodeDeploymentSpec, result2 error) {
fake.getChaincodeDepSpecMutex.Lock()
defer fake.getChaincodeDepSpecMutex.Unlock()
fake.GetChaincodeDepSpecStub = nil
if fake.getChaincodeDepSpecReturnsOnCall == nil {
fake.getChaincodeDepSpecReturnsOnCall = make(map[int]struct {
result1 *peer.ChaincodeDeploymentSpec
result2 error
})
}
fake.getChaincodeDepSpecReturnsOnCall[i] = struct {
result1 *peer.ChaincodeDeploymentSpec
result2 error
}{result1, result2}
}
func (fake *LegacyCCPackageLocator) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.getChaincodeDepSpecMutex.RLock()
defer fake.getChaincodeDepSpecMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *LegacyCCPackageLocator) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ persistence.LegacyCCPackageLocator = new(LegacyCCPackageLocator)
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mock
import mock "github.com/stretchr/testify/mock"
// MetadataProvider is an autogenerated mock type for the MetadataProvider type
type MetadataProvider struct {
mock.Mock
}
// GetDBArtifacts provides a mock function with given fields: codePackage
func (_m *MetadataProvider) GetDBArtifacts(codePackage []byte) ([]byte, error) {
ret := _m.Called(codePackage)
var r0 []byte
if rf, ok := ret.Get(0).(func([]byte) []byte); ok {
r0 = rf(codePackage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
var r1 error
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(codePackage)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"os"
"sync"
"time"
)
type OSFileInfo struct {
IsDirStub func() bool
isDirMutex sync.RWMutex
isDirArgsForCall []struct {
}
isDirReturns struct {
result1 bool
}
isDirReturnsOnCall map[int]struct {
result1 bool
}
ModTimeStub func() time.Time
modTimeMutex sync.RWMutex
modTimeArgsForCall []struct {
}
modTimeReturns struct {
result1 time.Time
}
modTimeReturnsOnCall map[int]struct {
result1 time.Time
}
ModeStub func() os.FileMode
modeMutex sync.RWMutex
modeArgsForCall []struct {
}
modeReturns struct {
result1 os.FileMode
}
modeReturnsOnCall map[int]struct {
result1 os.FileMode
}
NameStub func() string
nameMutex sync.RWMutex
nameArgsForCall []struct {
}
nameReturns struct {
result1 string
}
nameReturnsOnCall map[int]struct {
result1 string
}
SizeStub func() int64
sizeMutex sync.RWMutex
sizeArgsForCall []struct {
}
sizeReturns struct {
result1 int64
}
sizeReturnsOnCall map[int]struct {
result1 int64
}
SysStub func() interface{}
sysMutex sync.RWMutex
sysArgsForCall []struct {
}
sysReturns struct {
result1 interface{}
}
sysReturnsOnCall map[int]struct {
result1 interface{}
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *OSFileInfo) IsDir() bool {
fake.isDirMutex.Lock()
ret, specificReturn := fake.isDirReturnsOnCall[len(fake.isDirArgsForCall)]
fake.isDirArgsForCall = append(fake.isDirArgsForCall, struct {
}{})
fake.recordInvocation("IsDir", []interface{}{})
fake.isDirMutex.Unlock()
if fake.IsDirStub != nil {
return fake.IsDirStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.isDirReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) IsDirCallCount() int {
fake.isDirMutex.RLock()
defer fake.isDirMutex.RUnlock()
return len(fake.isDirArgsForCall)
}
func (fake *OSFileInfo) IsDirCalls(stub func() bool) {
fake.isDirMutex.Lock()
defer fake.isDirMutex.Unlock()
fake.IsDirStub = stub
}
func (fake *OSFileInfo) IsDirReturns(result1 bool) {
fake.isDirMutex.Lock()
defer fake.isDirMutex.Unlock()
fake.IsDirStub = nil
fake.isDirReturns = struct {
result1 bool
}{result1}
}
func (fake *OSFileInfo) IsDirReturnsOnCall(i int, result1 bool) {
fake.isDirMutex.Lock()
defer fake.isDirMutex.Unlock()
fake.IsDirStub = nil
if fake.isDirReturnsOnCall == nil {
fake.isDirReturnsOnCall = make(map[int]struct {
result1 bool
})
}
fake.isDirReturnsOnCall[i] = struct {
result1 bool
}{result1}
}
func (fake *OSFileInfo) ModTime() time.Time {
fake.modTimeMutex.Lock()
ret, specificReturn := fake.modTimeReturnsOnCall[len(fake.modTimeArgsForCall)]
fake.modTimeArgsForCall = append(fake.modTimeArgsForCall, struct {
}{})
fake.recordInvocation("ModTime", []interface{}{})
fake.modTimeMutex.Unlock()
if fake.ModTimeStub != nil {
return fake.ModTimeStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.modTimeReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) ModTimeCallCount() int {
fake.modTimeMutex.RLock()
defer fake.modTimeMutex.RUnlock()
return len(fake.modTimeArgsForCall)
}
func (fake *OSFileInfo) ModTimeCalls(stub func() time.Time) {
fake.modTimeMutex.Lock()
defer fake.modTimeMutex.Unlock()
fake.ModTimeStub = stub
}
func (fake *OSFileInfo) ModTimeReturns(result1 time.Time) {
fake.modTimeMutex.Lock()
defer fake.modTimeMutex.Unlock()
fake.ModTimeStub = nil
fake.modTimeReturns = struct {
result1 time.Time
}{result1}
}
func (fake *OSFileInfo) ModTimeReturnsOnCall(i int, result1 time.Time) {
fake.modTimeMutex.Lock()
defer fake.modTimeMutex.Unlock()
fake.ModTimeStub = nil
if fake.modTimeReturnsOnCall == nil {
fake.modTimeReturnsOnCall = make(map[int]struct {
result1 time.Time
})
}
fake.modTimeReturnsOnCall[i] = struct {
result1 time.Time
}{result1}
}
func (fake *OSFileInfo) Mode() os.FileMode {
fake.modeMutex.Lock()
ret, specificReturn := fake.modeReturnsOnCall[len(fake.modeArgsForCall)]
fake.modeArgsForCall = append(fake.modeArgsForCall, struct {
}{})
fake.recordInvocation("Mode", []interface{}{})
fake.modeMutex.Unlock()
if fake.ModeStub != nil {
return fake.ModeStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.modeReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) ModeCallCount() int {
fake.modeMutex.RLock()
defer fake.modeMutex.RUnlock()
return len(fake.modeArgsForCall)
}
func (fake *OSFileInfo) ModeCalls(stub func() os.FileMode) {
fake.modeMutex.Lock()
defer fake.modeMutex.Unlock()
fake.ModeStub = stub
}
func (fake *OSFileInfo) ModeReturns(result1 os.FileMode) {
fake.modeMutex.Lock()
defer fake.modeMutex.Unlock()
fake.ModeStub = nil
fake.modeReturns = struct {
result1 os.FileMode
}{result1}
}
func (fake *OSFileInfo) ModeReturnsOnCall(i int, result1 os.FileMode) {
fake.modeMutex.Lock()
defer fake.modeMutex.Unlock()
fake.ModeStub = nil
if fake.modeReturnsOnCall == nil {
fake.modeReturnsOnCall = make(map[int]struct {
result1 os.FileMode
})
}
fake.modeReturnsOnCall[i] = struct {
result1 os.FileMode
}{result1}
}
func (fake *OSFileInfo) Name() string {
fake.nameMutex.Lock()
ret, specificReturn := fake.nameReturnsOnCall[len(fake.nameArgsForCall)]
fake.nameArgsForCall = append(fake.nameArgsForCall, struct {
}{})
fake.recordInvocation("Name", []interface{}{})
fake.nameMutex.Unlock()
if fake.NameStub != nil {
return fake.NameStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.nameReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) NameCallCount() int {
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
return len(fake.nameArgsForCall)
}
func (fake *OSFileInfo) NameCalls(stub func() string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = stub
}
func (fake *OSFileInfo) NameReturns(result1 string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = nil
fake.nameReturns = struct {
result1 string
}{result1}
}
func (fake *OSFileInfo) NameReturnsOnCall(i int, result1 string) {
fake.nameMutex.Lock()
defer fake.nameMutex.Unlock()
fake.NameStub = nil
if fake.nameReturnsOnCall == nil {
fake.nameReturnsOnCall = make(map[int]struct {
result1 string
})
}
fake.nameReturnsOnCall[i] = struct {
result1 string
}{result1}
}
func (fake *OSFileInfo) Size() int64 {
fake.sizeMutex.Lock()
ret, specificReturn := fake.sizeReturnsOnCall[len(fake.sizeArgsForCall)]
fake.sizeArgsForCall = append(fake.sizeArgsForCall, struct {
}{})
fake.recordInvocation("Size", []interface{}{})
fake.sizeMutex.Unlock()
if fake.SizeStub != nil {
return fake.SizeStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.sizeReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) SizeCallCount() int {
fake.sizeMutex.RLock()
defer fake.sizeMutex.RUnlock()
return len(fake.sizeArgsForCall)
}
func (fake *OSFileInfo) SizeCalls(stub func() int64) {
fake.sizeMutex.Lock()
defer fake.sizeMutex.Unlock()
fake.SizeStub = stub
}
func (fake *OSFileInfo) SizeReturns(result1 int64) {
fake.sizeMutex.Lock()
defer fake.sizeMutex.Unlock()
fake.SizeStub = nil
fake.sizeReturns = struct {
result1 int64
}{result1}
}
func (fake *OSFileInfo) SizeReturnsOnCall(i int, result1 int64) {
fake.sizeMutex.Lock()
defer fake.sizeMutex.Unlock()
fake.SizeStub = nil
if fake.sizeReturnsOnCall == nil {
fake.sizeReturnsOnCall = make(map[int]struct {
result1 int64
})
}
fake.sizeReturnsOnCall[i] = struct {
result1 int64
}{result1}
}
func (fake *OSFileInfo) Sys() interface{} {
fake.sysMutex.Lock()
ret, specificReturn := fake.sysReturnsOnCall[len(fake.sysArgsForCall)]
fake.sysArgsForCall = append(fake.sysArgsForCall, struct {
}{})
fake.recordInvocation("Sys", []interface{}{})
fake.sysMutex.Unlock()
if fake.SysStub != nil {
return fake.SysStub()
}
if specificReturn {
return ret.result1
}
fakeReturns := fake.sysReturns
return fakeReturns.result1
}
func (fake *OSFileInfo) SysCallCount() int {
fake.sysMutex.RLock()
defer fake.sysMutex.RUnlock()
return len(fake.sysArgsForCall)
}
func (fake *OSFileInfo) SysCalls(stub func() interface{}) {
fake.sysMutex.Lock()
defer fake.sysMutex.Unlock()
fake.SysStub = stub
}
func (fake *OSFileInfo) SysReturns(result1 interface{}) {
fake.sysMutex.Lock()
defer fake.sysMutex.Unlock()
fake.SysStub = nil
fake.sysReturns = struct {
result1 interface{}
}{result1}
}
func (fake *OSFileInfo) SysReturnsOnCall(i int, result1 interface{}) {
fake.sysMutex.Lock()
defer fake.sysMutex.Unlock()
fake.SysStub = nil
if fake.sysReturnsOnCall == nil {
fake.sysReturnsOnCall = make(map[int]struct {
result1 interface{}
})
}
fake.sysReturnsOnCall[i] = struct {
result1 interface{}
}{result1}
}
func (fake *OSFileInfo) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.isDirMutex.RLock()
defer fake.isDirMutex.RUnlock()
fake.modTimeMutex.RLock()
defer fake.modTimeMutex.RUnlock()
fake.modeMutex.RLock()
defer fake.modeMutex.RUnlock()
fake.nameMutex.RLock()
defer fake.nameMutex.RUnlock()
fake.sizeMutex.RLock()
defer fake.sizeMutex.RUnlock()
fake.sysMutex.RLock()
defer fake.sysMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *OSFileInfo) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mock
import (
"bytes"
"compress/gzip"
fuzz "github.com/AdaLogics/go-fuzz-headers"
customBytes "github.com/AdamKorcz/bugdetectors/bytes"
"github.com/hyperledger/fabric/core/chaincode/persistence"
tm "github.com/stretchr/testify/mock"
"os"
)
func FuzzPersistence(data []byte) int {
var ccpp persistence.ChaincodePackageParser
mockMetaProvider := &MetadataProvider{}
mockMetaProvider.On("GetDBArtifacts", tm.Anything).Return([]byte("DB artefacts"), nil)
ccpp.MetadataProvider = mockMetaProvider
_, _ = ccpp.Parse(data)
return 1
}
func FuzzChaincodePackageStreamerMetadatabytes(data []byte) int {
err := os.WriteFile("demoTar.tar", data, 0666)
if err != nil {
return 0
}
defer os.Remove("demoTar.tar")
cps := &persistence.ChaincodePackageStreamer{PackagePath: "demoTar.tar"}
_, _ = cps.MetadataBytes()
return 1
}
func FuzzParseChaincodePackage(data []byte) int {
f := fuzz.NewConsumer(data)
source, err := f.TarBytes()
if err != nil {
return 0
}
var b bytes.Buffer
w := gzip.NewWriter(&b)
w.Write(source)
w.Close()
_, _, _ = persistence.ParseChaincodePackage(customBytes.CheckLen(b.Bytes(), "/src/fabric/core/chaincode/persistence/mock/persistence_fuzzer.go:56:46 (May be slightly inaccurate) NEW_LINEb.Bytes()"))
return 1
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package persistence
import (
"encoding/hex"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric/common/chaincode"
"github.com/hyperledger/fabric/common/util"
"github.com/pkg/errors"
)
var logger = flogging.MustGetLogger("chaincode.persistence")
// IOReadWriter defines the interface needed for reading, writing, removing, and
// checking for existence of a specified file
type IOReadWriter interface {
ReadDir(string) ([]os.FileInfo, error)
ReadFile(string) ([]byte, error)
Remove(name string) error
WriteFile(string, string, []byte) error
MakeDir(string, os.FileMode) error
Exists(path string) (bool, error)
}
// FilesystemIO is the production implementation of the IOWriter interface
type FilesystemIO struct{}
// WriteFile writes a file to the filesystem; it does so atomically
// by first writing to a temp file and then renaming the file so that
// if the operation crashes midway we're not stuck with a bad package
func (f *FilesystemIO) WriteFile(path, name string, data []byte) error {
if path == "" {
return errors.New("empty path not allowed")
}
tmpFile, err := os.CreateTemp(path, ".ccpackage.")
if err != nil {
return errors.Wrapf(err, "error creating temp file in directory '%s'", path)
}
defer os.Remove(tmpFile.Name())
if n, err := tmpFile.Write(data); err != nil || n != len(data) {
if err == nil {
err = errors.Errorf(
"failed to write the entire content of the file, expected %d, wrote %d",
len(data), n)
}
return errors.Wrapf(err, "error writing to temp file '%s'", tmpFile.Name())
}
if err := tmpFile.Close(); err != nil {
return errors.Wrapf(err, "error closing temp file '%s'", tmpFile.Name())
}
if err := os.Rename(tmpFile.Name(), filepath.Join(path, name)); err != nil {
return errors.Wrapf(err, "error renaming temp file '%s'", tmpFile.Name())
}
return nil
}
// Remove removes a file from the filesystem - used for rolling back an in-flight
// Save operation upon a failure
func (f *FilesystemIO) Remove(name string) error {
return os.Remove(name)
}
// ReadFile reads a file from the filesystem
func (f *FilesystemIO) ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
// ReadDir reads a directory from the filesystem
func (f *FilesystemIO) ReadDir(dirname string) ([]os.FileInfo, error) {
entries, err := os.ReadDir(dirname)
if err != nil {
return nil, err
}
infos := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return nil, err
}
infos = append(infos, info)
}
return infos, nil
}
// MakeDir makes a directory on the filesystem (and any
// necessary parent directories).
func (f *FilesystemIO) MakeDir(dirname string, mode os.FileMode) error {
return os.MkdirAll(dirname, mode)
}
// Exists checks whether a file exists
func (*FilesystemIO) Exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, errors.Wrapf(err, "could not determine whether file '%s' exists", path)
}
// Store holds the information needed for persisting a chaincode install package
type Store struct {
Path string
ReadWriter IOReadWriter
}
// NewStore creates a new chaincode persistence store using
// the provided path on the filesystem.
func NewStore(path string) *Store {
store := &Store{
Path: path,
ReadWriter: &FilesystemIO{},
}
store.Initialize()
return store
}
// Initialize checks for the existence of the _lifecycle chaincodes
// directory and creates it if it has not yet been created.
func (s *Store) Initialize() {
var (
exists bool
err error
)
if exists, err = s.ReadWriter.Exists(s.Path); exists {
return
}
if err != nil {
panic(fmt.Sprintf("Initialization of chaincode store failed: %s", err))
}
if err = s.ReadWriter.MakeDir(s.Path, 0o750); err != nil {
panic(fmt.Sprintf("Could not create _lifecycle chaincodes install path: %s", err))
}
}
// Save persists chaincode install package bytes. It returns
// the hash of the chaincode install package
func (s *Store) Save(label string, ccInstallPkg []byte) (string, error) {
packageID := PackageID(label, ccInstallPkg)
ccInstallPkgFileName := CCFileName(packageID)
ccInstallPkgFilePath := filepath.Join(s.Path, ccInstallPkgFileName)
if exists, _ := s.ReadWriter.Exists(ccInstallPkgFilePath); exists {
// chaincode install package was already installed
return packageID, nil
}
if err := s.ReadWriter.WriteFile(s.Path, ccInstallPkgFileName, ccInstallPkg); err != nil {
err = errors.Wrapf(err, "error writing chaincode install package to %s", ccInstallPkgFilePath)
logger.Error(err.Error())
return "", err
}
return packageID, nil
}
// Load loads a persisted chaincode install package bytes with
// the given packageID.
func (s *Store) Load(packageID string) ([]byte, error) {
ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
exists, err := s.ReadWriter.Exists(ccInstallPkgPath)
if err != nil {
return nil, errors.Wrapf(err, "could not determine whether chaincode install package '%s' exists", packageID)
}
if !exists {
return nil, &CodePackageNotFoundErr{
PackageID: packageID,
}
}
ccInstallPkg, err := s.ReadWriter.ReadFile(ccInstallPkgPath)
if err != nil {
err = errors.Wrapf(err, "error reading chaincode install package at %s", ccInstallPkgPath)
return nil, err
}
return ccInstallPkg, nil
}
// Delete deletes a persisted chaincode. Note, there is no locking,
// so this should only be performed if the chaincode has already
// been marked built.
func (s *Store) Delete(packageID string) error {
ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
return s.ReadWriter.Remove(ccInstallPkgPath)
}
// CodePackageNotFoundErr is the error returned when a code package cannot
// be found in the persistence store
type CodePackageNotFoundErr struct {
PackageID string
}
func (e CodePackageNotFoundErr) Error() string {
return fmt.Sprintf("chaincode install package '%s' not found", e.PackageID)
}
// ListInstalledChaincodes returns an array with information about the
// chaincodes installed in the persistence store
func (s *Store) ListInstalledChaincodes() ([]chaincode.InstalledChaincode, error) {
files, err := s.ReadWriter.ReadDir(s.Path)
if err != nil {
return nil, errors.Wrapf(err, "error reading chaincode directory at %s", s.Path)
}
installedChaincodes := []chaincode.InstalledChaincode{}
for _, file := range files {
if instCC, isInstCC := installedChaincodeFromFilename(file.Name()); isInstCC {
installedChaincodes = append(installedChaincodes, instCC)
}
}
return installedChaincodes, nil
}
// GetChaincodeInstallPath returns the path where chaincodes
// are installed
func (s *Store) GetChaincodeInstallPath() string {
return s.Path
}
// PackageID returns the package ID with the label and hash of the chaincode install package
func PackageID(label string, ccInstallPkg []byte) string {
hash := util.ComputeSHA256(ccInstallPkg)
return packageID(label, hash)
}
func packageID(label string, hash []byte) string {
return fmt.Sprintf("%s:%x", label, hash)
}
func CCFileName(packageID string) string {
return strings.Replace(packageID, ":", ".", 1) + ".tar.gz"
}
var packageFileMatcher = regexp.MustCompile("^(.+)[.]([0-9a-f]{64})[.]tar[.]gz$")
func installedChaincodeFromFilename(fileName string) (chaincode.InstalledChaincode, bool) {
matches := packageFileMatcher.FindStringSubmatch(fileName)
if len(matches) == 3 {
label := matches[1]
hash, _ := hex.DecodeString(matches[2])
packageID := packageID(label, hash)
return chaincode.InstalledChaincode{
Label: label,
Hash: hash,
PackageID: packageID,
}, true
}
return chaincode.InstalledChaincode{}, false
}
/*
Copyright IBM Corp. 2016-2019 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ccpackage
import (
"bytes"
"errors"
"fmt"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/internal/pkg/identity"
"github.com/hyperledger/fabric/protoutil"
"google.golang.org/protobuf/proto"
)
// ExtractSignedCCDepSpec extracts the messages from the envelope
func ExtractSignedCCDepSpec(env *common.Envelope) (*common.ChannelHeader, *peer.SignedChaincodeDeploymentSpec, error) {
p := &common.Payload{}
if err := proto.Unmarshal(env.Payload, p); err != nil {
return nil, nil, err
}
if p.Header == nil {
return nil, nil, errors.New("channel header cannot be nil")
}
ch := &common.ChannelHeader{}
if err := proto.Unmarshal(p.Header.ChannelHeader, ch); err != nil {
return nil, nil, err
}
sp := &peer.SignedChaincodeDeploymentSpec{}
if err := proto.Unmarshal(p.Data, sp); err != nil {
return nil, nil, err
}
return ch, sp, nil
}
// This file provides functions for helping with the chaincode install
// package workflow. In particular
// OwnerCreateSignedCCDepSpec - each owner creates signs the package using the same deploy
// CreateSignedCCDepSpecForInstall - an admin or owner creates the package to be installed
// using the packages from OwnerCreateSignedCCDepSpec
// ValidateCip validate the endorsed package against the base package
func ValidateCip(baseCip, otherCip *peer.SignedChaincodeDeploymentSpec) error {
if baseCip == nil || otherCip == nil {
panic("do not call with nil parameters")
}
if (baseCip.OwnerEndorsements == nil && otherCip.OwnerEndorsements != nil) || (baseCip.OwnerEndorsements != nil && otherCip.OwnerEndorsements == nil) {
return errors.New("endorsements should either be both nil or not nil")
}
bN := len(baseCip.OwnerEndorsements)
oN := len(otherCip.OwnerEndorsements)
if bN > 1 || oN > 1 {
return errors.New("expect utmost 1 endorsement from a owner")
}
if bN != oN {
return fmt.Errorf("Rule-all packages should be endorsed or none should be endorsed failed for (%d, %d)", bN, oN)
}
if !bytes.Equal(baseCip.ChaincodeDeploymentSpec, otherCip.ChaincodeDeploymentSpec) {
return fmt.Errorf("Rule-all deployment specs should match(%d, %d)", len(baseCip.ChaincodeDeploymentSpec), len(otherCip.ChaincodeDeploymentSpec))
}
if !bytes.Equal(baseCip.InstantiationPolicy, otherCip.InstantiationPolicy) {
return fmt.Errorf("Rule-all instantiation policies should match(%d, %d)", len(baseCip.InstantiationPolicy), len(otherCip.InstantiationPolicy))
}
return nil
}
func createSignedCCDepSpec(cdsbytes []byte, instpolicybytes []byte, endorsements []*peer.Endorsement) (*common.Envelope, error) {
if cdsbytes == nil {
return nil, errors.New("nil chaincode deployment spec")
}
if instpolicybytes == nil {
return nil, errors.New("nil instantiation policy")
}
// create SignedChaincodeDeploymentSpec...
cip := &peer.SignedChaincodeDeploymentSpec{ChaincodeDeploymentSpec: cdsbytes, InstantiationPolicy: instpolicybytes, OwnerEndorsements: endorsements}
// ...and marshal it
cipbytes := protoutil.MarshalOrPanic(cip)
// use defaults (this is definitely ok for install package)
msgVersion := int32(0)
epoch := uint64(0)
chdr := protoutil.MakeChannelHeader(common.HeaderType_CHAINCODE_PACKAGE, msgVersion, "", epoch)
// create the payload
payl := &common.Payload{Header: &common.Header{ChannelHeader: protoutil.MarshalOrPanic(chdr)}, Data: cipbytes}
paylBytes, err := protoutil.GetBytesPayload(payl)
if err != nil {
return nil, err
}
// here's the unsigned envelope. The install package is endorsed if signingEntity != nil
return &common.Envelope{Payload: paylBytes}, nil
}
// CreateSignedCCDepSpecForInstall creates the final package from a set of packages signed by
// owners. This is similar to how the SDK assembles a TX from various proposal
// responses from the signatures.
func CreateSignedCCDepSpecForInstall(pack []*common.Envelope) (*common.Envelope, error) {
if len(pack) == 0 {
return nil, errors.New("no packages provided to collate")
}
// rules...
// all packages must be endorsed or all packages should not be endorsed
// the chaincode deployment spec should be same
var baseCip *peer.SignedChaincodeDeploymentSpec
var err error
var endorsementExists bool
var endorsements []*peer.Endorsement
for n, r := range pack {
p := &common.Payload{}
if err = proto.Unmarshal(r.Payload, p); err != nil {
return nil, err
}
cip := &peer.SignedChaincodeDeploymentSpec{}
if err = proto.Unmarshal(p.Data, cip); err != nil {
return nil, err
}
// if its the first element, check if it has endorsement so we can
// enforce endorsement rules
if n == 0 {
baseCip = cip
// if it has endorsement, all other owners should have signed too
if len(cip.OwnerEndorsements) > 0 {
endorsementExists = true
endorsements = make([]*peer.Endorsement, len(pack))
}
} else if err = ValidateCip(baseCip, cip); err != nil {
return nil, err
}
if endorsementExists {
endorsements[n] = cip.OwnerEndorsements[0]
}
}
return createSignedCCDepSpec(baseCip.ChaincodeDeploymentSpec, baseCip.InstantiationPolicy, endorsements)
}
// OwnerCreateSignedCCDepSpec creates a package from a ChaincodeDeploymentSpec and
// optionally endorses it
func OwnerCreateSignedCCDepSpec(cds *peer.ChaincodeDeploymentSpec, instPolicy *common.SignaturePolicyEnvelope, owner identity.SignerSerializer) (*common.Envelope, error) {
if cds == nil {
return nil, errors.New("invalid chaincode deployment spec")
}
if instPolicy == nil {
return nil, errors.New("must provide an instantiation policy")
}
cdsbytes := protoutil.MarshalOrPanic(cds)
instpolicybytes := protoutil.MarshalOrPanic(instPolicy)
var endorsements []*peer.Endorsement
// it is not mandatory (at this protoutil level) to have a signature
// this is especially convenient during dev/test
// it may be necessary to enforce it via a policy at a higher level
if owner != nil {
// serialize the signing identity
endorser, err := owner.Serialize()
if err != nil {
return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
}
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
signature, err := owner.Sign(append(cdsbytes, append(instpolicybytes, endorser...)...))
if err != nil {
return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
}
// each owner starts off the endorsements with one element. All such endorsed
// packages will be collected in a final package by CreateSignedCCDepSpecForInstall
// when endorsements will have all the entries
endorsements = make([]*peer.Endorsement, 1)
endorsements[0] = &peer.Endorsement{Signature: signature, Endorser: endorser}
}
return createSignedCCDepSpec(cdsbytes, instpolicybytes, endorsements)
}
// SignExistingPackage adds a signature to a signed package.
func SignExistingPackage(env *common.Envelope, owner identity.SignerSerializer) (*common.Envelope, error) {
if owner == nil {
return nil, errors.New("owner not provided")
}
ch, sdepspec, err := ExtractSignedCCDepSpec(env)
if err != nil {
return nil, err
}
if ch == nil {
return nil, errors.New("channel header not found in the envelope")
}
if sdepspec == nil || sdepspec.ChaincodeDeploymentSpec == nil || sdepspec.InstantiationPolicy == nil || sdepspec.OwnerEndorsements == nil {
return nil, errors.New("invalid signed deployment spec")
}
// serialize the signing identity
endorser, err := owner.Serialize()
if err != nil {
return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
}
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
signature, err := owner.Sign(append(sdepspec.ChaincodeDeploymentSpec, append(sdepspec.InstantiationPolicy, endorser...)...))
if err != nil {
return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
}
endorsements := append(sdepspec.OwnerEndorsements, &peer.Endorsement{Signature: signature, Endorser: endorser})
return createSignedCCDepSpec(sdepspec.ChaincodeDeploymentSpec, sdepspec.InstantiationPolicy, endorsements)
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ccprovider
import (
"archive/tar"
"bytes"
"errors"
"io"
"path/filepath"
"strings"
io2 "github.com/AdamKorcz/bugdetectors/io"
)
// TarFileEntry encapsulates a file entry and it's contents inside a tar
type TarFileEntry struct {
FileHeader *tar.Header
FileContent []byte
}
// ExtractStatedbArtifactsForChaincode extracts the statedb artifacts from the code package tar and create a statedb artifact tar.
// The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb.
// This function is intended to be used during chaincode instantiate/upgrade so that statedb artifacts can be created.
func ExtractStatedbArtifactsForChaincode(ccNameVersion string) (installed bool, statedbArtifactsTar []byte, err error) {
ccpackage, err := GetChaincodeFromFS(ccNameVersion)
if err != nil {
// TODO for now, we assume that an error indicates that the chaincode is not installed on the peer.
// However, we need a way to differentiate between the 'not installed' and a general error so that on general error,
// we can abort the chaincode instantiate/upgrade/install operation.
ccproviderLogger.Infof("Error while loading installation package for ccNameVersion=%s Err=%s", ccNameVersion, err)
return false, nil, nil
}
statedbArtifactsTar, err = ExtractStatedbArtifactsFromCCPackage(ccpackage)
return true, statedbArtifactsTar, err
}
// ExtractStatedbArtifactsFromCCPackage extracts the statedb artifacts from the code package tar and create a statedb artifact tar.
// The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb.
// This function is called during chaincode instantiate/upgrade (from above), and from install, so that statedb artifacts can be created.
func ExtractStatedbArtifactsFromCCPackage(ccpackage CCPackage) (statedbArtifactsTar []byte, err error) {
cds := ccpackage.GetDepSpec()
metaprov, err := MetadataAsTarEntries(cds.CodePackage)
if err != nil {
ccproviderLogger.Infof("invalid deployment spec: %s", err)
return nil, errors.New("invalid deployment spec")
}
return metaprov, nil
}
// ExtractFileEntries extract file entries from the given `tarBytes`. A file entry is included in the
// returned results only if it is located in a directory under the indicated databaseType directory
// Example for chaincode indexes:
// "META-INF/statedb/couchdb/indexes/indexColorSortName.json"
// Example for collection scoped indexes:
// "META-INF/statedb/couchdb/collections/collectionMarbles/indexes/indexCollMarbles.json"
// An empty string will have the effect of returning all statedb metadata. This is useful in validating an
// archive in the future with multiple database types
func ExtractFileEntries(tarBytes []byte, databaseType string) (map[string][]*TarFileEntry, error) {
indexArtifacts := map[string][]*TarFileEntry{}
tarReader := tar.NewReader(bytes.NewReader(tarBytes))
for {
hdr, err := tarReader.Next()
if err == io.EOF {
// end of tar archive
break
}
if err != nil {
return nil, err
}
if hdr.Typeflag == tar.TypeDir {
continue
}
// split the directory from the full name
dir, _ := filepath.Split(hdr.Name)
// remove the ending slash
if strings.HasPrefix(hdr.Name, "META-INF/statedb/"+databaseType) {
fileContent, err := io2.ReadAll(tarReader, "/src/fabric/core/common/ccprovider/cc_statedb_artifacts_provider.go:83:24 (May be slightly inaccurate) NEW_LINEio.ReadAll", true)
if err != nil {
return nil, err
}
indexArtifacts[filepath.Clean(dir)] = append(indexArtifacts[filepath.Clean(dir)], &TarFileEntry{FileHeader: hdr, FileContent: fileContent})
}
}
return indexArtifacts, nil
}
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ccprovider
import (
"fmt"
"sync"
)
// ccInfoCacheImpl implements in-memory cache for ChaincodeData
// needed by endorser to verify if the local instantiation policy
// matches the instantiation policy on a channel before honoring
// an invoke
type ccInfoCacheImpl struct {
sync.RWMutex
cache map[string]*ChaincodeData
cacheSupport CCCacheSupport
}
// NewCCInfoCache returns a new cache on top of the supplied CCInfoProvider instance
func NewCCInfoCache(cs CCCacheSupport) *ccInfoCacheImpl {
return &ccInfoCacheImpl{
cache: make(map[string]*ChaincodeData),
cacheSupport: cs,
}
}
func (c *ccInfoCacheImpl) GetChaincodeData(ccNameVersion string) (*ChaincodeData, error) {
// c.cache is guaranteed to be non-nil
c.RLock()
ccdata, in := c.cache[ccNameVersion]
c.RUnlock()
if !in {
// the chaincode data is not in the cache
// try to look it up from the file system
ccpack, err := c.cacheSupport.GetChaincode(ccNameVersion)
if err != nil || ccpack == nil {
return nil, fmt.Errorf("cannot retrieve package for chaincode %ss, error %s", ccNameVersion, err)
}
// we have a non-nil ChaincodeData, put it in the cache
c.Lock()
ccdata = ccpack.GetChaincodeData()
c.cache[ccNameVersion] = ccdata
c.Unlock()
}
return ccdata, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ccprovider
import (
"fmt"
"os"
"path/filepath"
"strings"
"unicode"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/factory"
"github.com/hyperledger/fabric-lib-go/common/flogging"
pb "github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/common/chaincode"
"github.com/hyperledger/fabric/core/common/privdata"
"github.com/hyperledger/fabric/core/ledger"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
var ccproviderLogger = flogging.MustGetLogger("ccprovider")
var chaincodeInstallPath string
// CCPackage encapsulates a chaincode package which can be
//
// raw ChaincodeDeploymentSpec
// SignedChaincodeDeploymentSpec
//
// Attempt to keep the interface at a level with minimal
// interface for possible generalization.
type CCPackage interface {
// InitFromBuffer initialize the package from bytes
InitFromBuffer(buf []byte) (*ChaincodeData, error)
// PutChaincodeToFS writes the chaincode to the filesystem
PutChaincodeToFS() error
// GetDepSpec gets the ChaincodeDeploymentSpec from the package
GetDepSpec() *pb.ChaincodeDeploymentSpec
// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
GetDepSpecBytes() []byte
// ValidateCC validates and returns the chaincode deployment spec corresponding to
// ChaincodeData. The validation is based on the metadata from ChaincodeData
// One use of this method is to validate the chaincode before launching
ValidateCC(ccdata *ChaincodeData) error
// GetPackageObject gets the object as a proto.Message
GetPackageObject() proto.Message
// GetChaincodeData gets the ChaincodeData
GetChaincodeData() *ChaincodeData
// GetId gets the fingerprint of the chaincode based on package computation
GetId() []byte
}
// SetChaincodesPath sets the chaincode path for this peer
func SetChaincodesPath(path string) {
if s, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
if err := os.Mkdir(path, 0o755); err != nil {
panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
}
} else {
panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
}
} else if !s.IsDir() {
panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
}
chaincodeInstallPath = path
}
// isPrintable is used by CDSPackage and SignedCDSPackage validation to
// detect garbage strings in unmarshaled proto fields where printable
// characters are expected.
func isPrintable(name string) bool {
notASCII := func(r rune) bool {
return !unicode.IsPrint(r)
}
return strings.IndexFunc(name, notASCII) == -1
}
// GetChaincodePackageFromPath returns the chaincode package from the file system
func GetChaincodePackageFromPath(ccNameVersion string, ccInstallPath string) ([]byte, error) {
path := fmt.Sprintf("%s/%s", ccInstallPath, strings.ReplaceAll(ccNameVersion, ":", "."))
var ccbytes []byte
var err error
if ccbytes, err = os.ReadFile(path); err != nil {
return nil, err
}
return ccbytes, nil
}
// ChaincodePackageExists returns whether the chaincode package exists in the file system
func ChaincodePackageExists(ccname string, ccversion string) (bool, error) {
path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion)
_, err := os.Stat(path)
if err == nil {
// chaincodepackage already exists
return true, nil
}
return false, err
}
type CCCacheSupport interface {
// GetChaincode is needed by the cache to get chaincode data
GetChaincode(ccNameVersion string) (CCPackage, error)
}
// CCInfoFSImpl provides the implementation for CC on the FS and the access to it
// It implements CCCacheSupport
type CCInfoFSImpl struct {
GetHasher GetHasher
}
// GetChaincode this is a wrapper for hiding package implementation.
// It calls GetChaincodeFromPath with the chaincodeInstallPath
func (cifs *CCInfoFSImpl) GetChaincode(ccNameVersion string) (CCPackage, error) {
return cifs.GetChaincodeFromPath(ccNameVersion, chaincodeInstallPath)
}
func (cifs *CCInfoFSImpl) GetChaincodeCodePackage(ccNameVersion string) ([]byte, error) {
ccpack, err := cifs.GetChaincode(ccNameVersion)
if err != nil {
return nil, err
}
return ccpack.GetDepSpec().CodePackage, nil
}
func (cifs *CCInfoFSImpl) GetChaincodeDepSpec(ccNameVersion string) (*pb.ChaincodeDeploymentSpec, error) {
ccpack, err := cifs.GetChaincode(ccNameVersion)
if err != nil {
return nil, err
}
return ccpack.GetDepSpec(), nil
}
// GetChaincodeFromPath this is a wrapper for hiding package implementation.
func (cifs *CCInfoFSImpl) GetChaincodeFromPath(ccNameVersion string, path string) (CCPackage, error) {
// try raw CDS
cccdspack := &CDSPackage{GetHasher: cifs.GetHasher}
_, _, err := cccdspack.InitFromPath(ccNameVersion, path)
if err != nil {
// try signed CDS
ccscdspack := &SignedCDSPackage{GetHasher: cifs.GetHasher}
_, _, err = ccscdspack.InitFromPath(ccNameVersion, path)
if err != nil {
return nil, err
}
return ccscdspack, nil
}
return cccdspack, nil
}
// GetChaincodeInstallPath returns the path to the installed chaincodes
func (*CCInfoFSImpl) GetChaincodeInstallPath() string {
return chaincodeInstallPath
}
// PutChaincode is a wrapper for putting raw ChaincodeDeploymentSpec
// using CDSPackage. This is only used in UTs
func (cifs *CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) {
buf, err := proto.Marshal(depSpec)
if err != nil {
return nil, err
}
cccdspack := &CDSPackage{GetHasher: cifs.GetHasher}
if _, err := cccdspack.InitFromBuffer(buf); err != nil {
return nil, err
}
err = cccdspack.PutChaincodeToFS()
if err != nil {
return nil, err
}
return cccdspack, nil
}
// DirEnumerator enumerates directories
type DirEnumerator func(string) ([]os.DirEntry, error)
// ChaincodeExtractor extracts chaincode from a given path
type ChaincodeExtractor func(ccNameVersion string, path string, getHasher GetHasher) (CCPackage, error)
// ListInstalledChaincodes retrieves the installed chaincodes
func (cifs *CCInfoFSImpl) ListInstalledChaincodes(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) ([]chaincode.InstalledChaincode, error) {
var chaincodes []chaincode.InstalledChaincode
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
return nil, nil
}
files, err := ls(dir)
if err != nil {
return nil, errors.Wrapf(err, "failed reading directory %s", dir)
}
for _, f := range files {
// Skip directories, we're only interested in normal files
if f.IsDir() {
continue
}
// A chaincode file name is of the type "name.version"
// We're only interested in the name.
// Skip files that don't adhere to the file naming convention of "A.B"
i := strings.Index(f.Name(), ".")
if i == -1 {
ccproviderLogger.Info("Skipping", f.Name(), "because of missing separator '.'")
continue
}
ccName := f.Name()[:i] // Everything before the separator
ccVersion := f.Name()[i+1:] // Everything after the separator
ccPackage, err := ccFromPath(ccName+":"+ccVersion, dir, cifs.GetHasher)
if err != nil {
ccproviderLogger.Warning("Failed obtaining chaincode information about", ccName, ccVersion, ":", err)
return nil, errors.Wrapf(err, "failed obtaining information about %s, version %s", ccName, ccVersion)
}
chaincodes = append(chaincodes, chaincode.InstalledChaincode{
Name: ccName,
Version: ccVersion,
Hash: ccPackage.GetId(),
})
}
ccproviderLogger.Debug("Returning", chaincodes)
return chaincodes, nil
}
// ccInfoFSStorageMgr is the storage manager used either by the cache or if the
// cache is bypassed
var ccInfoFSProvider = &CCInfoFSImpl{GetHasher: factory.GetDefault()}
// ccInfoCache is the cache instance itself
var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
// GetChaincodeFromFS retrieves chaincode information from the file system
func GetChaincodeFromFS(ccNameVersion string) (CCPackage, error) {
return ccInfoFSProvider.GetChaincode(ccNameVersion)
}
// GetChaincodeData gets chaincode data from cache if there's one
func GetChaincodeData(ccNameVersion string) (*ChaincodeData, error) {
ccproviderLogger.Debugf("Getting chaincode data for <%s> from cache", ccNameVersion)
return ccInfoCache.GetChaincodeData(ccNameVersion)
}
// GetCCPackage tries each known package implementation one by one
// till the right package is found
func GetCCPackage(buf []byte, bccsp bccsp.BCCSP) (CCPackage, error) {
// try raw CDS
cds := &CDSPackage{GetHasher: bccsp}
if ccdata, err := cds.InitFromBuffer(buf); err != nil {
cds = nil
} else {
err = cds.ValidateCC(ccdata)
if err != nil {
cds = nil
}
}
// try signed CDS
scds := &SignedCDSPackage{GetHasher: bccsp}
if ccdata, err := scds.InitFromBuffer(buf); err != nil {
scds = nil
} else {
err = scds.ValidateCC(ccdata)
if err != nil {
scds = nil
}
}
if cds != nil && scds != nil {
// Both were unmarshaled successfully, this is exactly why the approach of
// hoping proto fails for bad inputs is fatally flawed.
ccproviderLogger.Errorf("Could not determine chaincode package type, guessing SignedCDS")
return scds, nil
}
if cds != nil {
return cds, nil
}
if scds != nil {
return scds, nil
}
return nil, errors.New("could not unmarshal chaincode package to CDS or SignedCDS")
}
// GetInstalledChaincodes returns a map whose key is the chaincode id and
// value is the ChaincodeDeploymentSpec struct for that chaincodes that have
// been installed (but not necessarily instantiated) on the peer by searching
// the chaincode install path
func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
files, err := os.ReadDir(chaincodeInstallPath)
if err != nil {
return nil, err
}
// array to store info for all chaincode entries from LSCC
var ccInfoArray []*pb.ChaincodeInfo
for _, file := range files {
// split at first period as chaincode versions can contain periods while
// chaincode names cannot
fileNameArray := strings.SplitN(file.Name(), ".", 2)
// check that length is 2 as expected, otherwise skip to next cc file
if len(fileNameArray) == 2 {
ccname := fileNameArray[0]
ccversion := fileNameArray[1]
ccpack, err := GetChaincodeFromFS(ccname + ":" + ccversion)
if err != nil {
// either chaincode on filesystem has been tampered with or
// _lifecycle chaincode files exist in the chaincodes directory.
continue
}
cdsfs := ccpack.GetDepSpec()
name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
if name != ccname || version != ccversion {
// chaincode name/version in the chaincode file name has been modified
// by an external entity
ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
continue
}
path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
// since this is just an installed chaincode these should be blank
input, escc, vscc := "", "", ""
ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc, Id: ccpack.GetId()}
// add this specific chaincode's metadata to the array of all chaincodes
ccInfoArray = append(ccInfoArray, ccInfo)
}
}
// add array with info about all instantiated chaincodes to the query
// response proto
cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
return cqr, nil
}
// ChaincodeID is the name by which the chaincode will register itself.
func (cd *ChaincodeData) ChaincodeID() string {
return cd.Name + ":" + cd.Version
}
// TransactionParams are parameters which are tied to a particular transaction
// and which are required for invoking chaincode.
type TransactionParams struct {
TxID string
ChannelID string
NamespaceID string
SignedProp *pb.SignedProposal
Proposal *pb.Proposal
TXSimulator ledger.TxSimulator
HistoryQueryExecutor ledger.HistoryQueryExecutor
CollectionStore privdata.CollectionStore
IsInitTransaction bool
// this is additional data passed to the chaincode
ProposalDecorations map[string][]byte
}
//
//Copyright IBM Corp. All Rights Reserved.
//SPDX-License-Identifier: Apache-2.0
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.34.2
// protoc v5.27.3
// source: ccprovider.proto
package ccprovider
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// -------- ChaincodeData is stored on the LSCC -------
// ChaincodeData defines the datastructure for chaincodes to be serialized by proto
// Type provides an additional check by directing to use a specific package after instantiation
// Data is Type specific (see CDSPackage and SignedCDSPackage)
type ChaincodeData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Name of the chaincode
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Version of the chaincode
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// Escc for the chaincode instance
Escc string `protobuf:"bytes,3,opt,name=escc,proto3" json:"escc,omitempty"`
// Vscc for the chaincode instance
Vscc string `protobuf:"bytes,4,opt,name=vscc,proto3" json:"vscc,omitempty"`
// Policy endorsement policy for the chaincode instance
Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3" json:"policy,omitempty"`
// Data data specific to the package
Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"`
// Id of the chaincode that's the unique fingerprint for the CC This is not
// currently used anywhere but serves as a good eyecatcher
Id []byte `protobuf:"bytes,7,opt,name=id,proto3" json:"id,omitempty"`
// InstantiationPolicy for the chaincode
InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,json=instantiationPolicy,proto3" json:"instantiation_policy,omitempty"`
}
func (x *ChaincodeData) Reset() {
*x = ChaincodeData{}
if protoimpl.UnsafeEnabled {
mi := &file_ccprovider_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChaincodeData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChaincodeData) ProtoMessage() {}
func (x *ChaincodeData) ProtoReflect() protoreflect.Message {
mi := &file_ccprovider_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChaincodeData.ProtoReflect.Descriptor instead.
func (*ChaincodeData) Descriptor() ([]byte, []int) {
return file_ccprovider_proto_rawDescGZIP(), []int{0}
}
func (x *ChaincodeData) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ChaincodeData) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *ChaincodeData) GetEscc() string {
if x != nil {
return x.Escc
}
return ""
}
func (x *ChaincodeData) GetVscc() string {
if x != nil {
return x.Vscc
}
return ""
}
func (x *ChaincodeData) GetPolicy() []byte {
if x != nil {
return x.Policy
}
return nil
}
func (x *ChaincodeData) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
func (x *ChaincodeData) GetId() []byte {
if x != nil {
return x.Id
}
return nil
}
func (x *ChaincodeData) GetInstantiationPolicy() []byte {
if x != nil {
return x.InstantiationPolicy
}
return nil
}
// ----- CDSData ------
// CDSData is data stored in the LSCC on instantiation of a CC
// for CDSPackage. This needs to be serialized for ChaincodeData
// hence the protobuf format
type CDSData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// CodeHash hash of CodePackage from ChaincodeDeploymentSpec
CodeHash []byte `protobuf:"bytes,1,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"`
// MetaDataHash hash of Name and Version from ChaincodeDeploymentSpec
MetaDataHash []byte `protobuf:"bytes,2,opt,name=meta_data_hash,json=metaDataHash,proto3" json:"meta_data_hash,omitempty"`
}
func (x *CDSData) Reset() {
*x = CDSData{}
if protoimpl.UnsafeEnabled {
mi := &file_ccprovider_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CDSData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CDSData) ProtoMessage() {}
func (x *CDSData) ProtoReflect() protoreflect.Message {
mi := &file_ccprovider_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CDSData.ProtoReflect.Descriptor instead.
func (*CDSData) Descriptor() ([]byte, []int) {
return file_ccprovider_proto_rawDescGZIP(), []int{1}
}
func (x *CDSData) GetCodeHash() []byte {
if x != nil {
return x.CodeHash
}
return nil
}
func (x *CDSData) GetMetaDataHash() []byte {
if x != nil {
return x.MetaDataHash
}
return nil
}
// ----- SignedCDSData ------
// SignedCDSData is data stored in the LSCC on instantiation of a CC
// for SignedCDSPackage. This needs to be serialized for ChaincodeData
// hence the protobuf format
type SignedCDSData struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
CodeHash []byte `protobuf:"bytes,1,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"`
MetaDataHash []byte `protobuf:"bytes,2,opt,name=meta_data_hash,json=metaDataHash,proto3" json:"meta_data_hash,omitempty"`
SignatureHash []byte `protobuf:"bytes,3,opt,name=signature_hash,json=signatureHash,proto3" json:"signature_hash,omitempty"`
}
func (x *SignedCDSData) Reset() {
*x = SignedCDSData{}
if protoimpl.UnsafeEnabled {
mi := &file_ccprovider_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SignedCDSData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SignedCDSData) ProtoMessage() {}
func (x *SignedCDSData) ProtoReflect() protoreflect.Message {
mi := &file_ccprovider_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SignedCDSData.ProtoReflect.Descriptor instead.
func (*SignedCDSData) Descriptor() ([]byte, []int) {
return file_ccprovider_proto_rawDescGZIP(), []int{2}
}
func (x *SignedCDSData) GetCodeHash() []byte {
if x != nil {
return x.CodeHash
}
return nil
}
func (x *SignedCDSData) GetMetaDataHash() []byte {
if x != nil {
return x.MetaDataHash
}
return nil
}
func (x *SignedCDSData) GetSignatureHash() []byte {
if x != nil {
return x.SignatureHash
}
return nil
}
var File_ccprovider_proto protoreflect.FileDescriptor
var file_ccprovider_proto_rawDesc = []byte{
0x0a, 0x10, 0x63, 0x63, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x0a, 0x63, 0x63, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0xd4,
0x01, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12,
0x0a, 0x04, 0x65, 0x73, 0x63, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x73,
0x63, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x73, 0x63, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x76, 0x73, 0x63, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x12,
0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61,
0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02,
0x69, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x13, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x4c, 0x0a, 0x07, 0x43, 0x44, 0x53, 0x44, 0x61, 0x74, 0x61,
0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a,
0x0e, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x48,
0x61, 0x73, 0x68, 0x22, 0x79, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x44, 0x53,
0x44, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x68, 0x61, 0x73,
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x48, 0x61, 0x73,
0x68, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68,
0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x44,
0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x74, 0x75, 0x72, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x48, 0x61, 0x73, 0x68, 0x42, 0x36,
0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x79, 0x70,
0x65, 0x72, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f,
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x63, 0x70, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_ccprovider_proto_rawDescOnce sync.Once
file_ccprovider_proto_rawDescData = file_ccprovider_proto_rawDesc
)
func file_ccprovider_proto_rawDescGZIP() []byte {
file_ccprovider_proto_rawDescOnce.Do(func() {
file_ccprovider_proto_rawDescData = protoimpl.X.CompressGZIP(file_ccprovider_proto_rawDescData)
})
return file_ccprovider_proto_rawDescData
}
var file_ccprovider_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_ccprovider_proto_goTypes = []any{
(*ChaincodeData)(nil), // 0: ccprovider.ChaincodeData
(*CDSData)(nil), // 1: ccprovider.CDSData
(*SignedCDSData)(nil), // 2: ccprovider.SignedCDSData
}
var file_ccprovider_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_ccprovider_proto_init() }
func file_ccprovider_proto_init() {
if File_ccprovider_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_ccprovider_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*ChaincodeData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_ccprovider_proto_msgTypes[1].Exporter = func(v any, i int) any {
switch v := v.(*CDSData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_ccprovider_proto_msgTypes[2].Exporter = func(v any, i int) any {
switch v := v.(*SignedCDSData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_ccprovider_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_ccprovider_proto_goTypes,
DependencyIndexes: file_ccprovider_proto_depIdxs,
MessageInfos: file_ccprovider_proto_msgTypes,
}.Build()
File_ccprovider_proto = out.File
file_ccprovider_proto_rawDesc = nil
file_ccprovider_proto_goTypes = nil
file_ccprovider_proto_depIdxs = nil
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ccprovider
import (
fuzz "github.com/AdaLogics/go-fuzz-headers"
)
func FuzzExtractFileEntries(data []byte) int {
f := fuzz.NewConsumer(data)
tarBytes, err := f.TarBytes()
if err != nil {
return 0
}
databaseType, err := f.GetString()
if err != nil {
return 0
}
_, _ = ExtractFileEntries(tarBytes, databaseType)
return 1
}
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ccprovider
import (
"errors"
"fmt"
"hash"
"os"
"github.com/hyperledger/fabric-lib-go/bccsp"
pb "github.com/hyperledger/fabric-protos-go-apiv2/peer"
"google.golang.org/protobuf/proto"
)
// GetHasher interface defines a subset of bccsp which contains GetHash function.
type GetHasher interface {
GetHash(opts bccsp.HashOpts) (h hash.Hash, err error)
}
// --------- CDSPackage ------------
// CDSPackage encapsulates ChaincodeDeploymentSpec.
type CDSPackage struct {
buf []byte
depSpec *pb.ChaincodeDeploymentSpec
data *CDSData
datab []byte
id []byte
GetHasher GetHasher
}
// GetId gets the fingerprint of the chaincode based on package computation
func (ccpack *CDSPackage) GetId() []byte {
// this has to be after creating a package and initializing it
// If those steps fail, GetId() should never be called
if ccpack.id == nil {
panic("GetId called on uninitialized package")
}
return ccpack.id
}
// GetDepSpec gets the ChaincodeDeploymentSpec from the package
func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
// this has to be after creating a package and initializing it
// If those steps fail, GetDepSpec() should never be called
if ccpack.depSpec == nil {
panic("GetDepSpec called on uninitialized package")
}
return ccpack.depSpec
}
// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
func (ccpack *CDSPackage) GetDepSpecBytes() []byte {
// this has to be after creating a package and initializing it
// If those steps fail, GetDepSpecBytes() should never be called
if ccpack.buf == nil {
panic("GetDepSpecBytes called on uninitialized package")
}
return ccpack.buf
}
// GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
func (ccpack *CDSPackage) GetPackageObject() proto.Message {
return ccpack.depSpec
}
// GetChaincodeData gets the ChaincodeData
func (ccpack *CDSPackage) GetChaincodeData() *ChaincodeData {
// this has to be after creating a package and initializing it
// If those steps fail, GetChaincodeData() should never be called
if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil {
panic("GetChaincodeData called on uninitialized package")
}
return &ChaincodeData{Name: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name, Version: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version, Data: ccpack.datab, Id: ccpack.id}
}
func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, []byte, *CDSData, error) {
// check for nil argument. It is an assertion that getCDSData
// is never called on a package that did not go through/succeed
// package initialization.
if cds == nil {
panic("nil cds")
}
if _, err := proto.Marshal(cds); err != nil {
return nil, nil, nil, err
}
// compute hashes now
// hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{})
if err != nil {
return nil, nil, nil, err
}
cdsdata := &CDSData{}
// code hash
hash.Write(cds.CodePackage)
cdsdata.CodeHash = hash.Sum(nil)
hash.Reset()
// metadata hash
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
cdsdata.MetaDataHash = hash.Sum(nil)
b, err := proto.Marshal(cdsdata)
if err != nil {
return nil, nil, nil, err
}
hash.Reset()
// compute the id
hash.Write(cdsdata.CodeHash)
hash.Write(cdsdata.MetaDataHash)
id := hash.Sum(nil)
return b, id, cdsdata, nil
}
// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
if ccpack.depSpec == nil {
return errors.New("uninitialized package")
}
if ccpack.data == nil {
return errors.New("nil data")
}
// This is a hack. LSCC expects a specific LSCC error when names are invalid so it
// has its own validation code. We can't use that error because of import cycles.
// Unfortunately, we also need to check if what have makes some sort of sense as
// protobuf will gladly deserialize garbage and there are paths where we assume that
// a successful unmarshal means everything works but, if it fails, we try to unmarshal
// into something different.
if !isPrintable(ccdata.Name) {
return fmt.Errorf("invalid chaincode name: %q", ccdata.Name)
}
if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
}
otherdata := &CDSData{}
err := proto.Unmarshal(ccdata.Data, otherdata)
if err != nil {
return err
}
if !proto.Equal(ccpack.data, otherdata) {
return errors.New("data mismatch")
}
return nil
}
// InitFromBuffer sets the buffer if valid and returns ChaincodeData
func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
depSpec := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(buf, depSpec)
if err != nil {
return nil, errors.New("failed to unmarshal deployment spec from bytes")
}
databytes, id, data, err := ccpack.getCDSData(depSpec)
if err != nil {
return nil, err
}
ccpack.buf = buf
ccpack.depSpec = depSpec
ccpack.data = data
ccpack.datab = databytes
ccpack.id = id
return ccpack.GetChaincodeData(), nil
}
// InitFromPath returns the chaincode and its package from the file system
func (ccpack *CDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
buf, err := GetChaincodePackageFromPath(ccNameVersion, path)
if err != nil {
return nil, nil, err
}
ccdata, err := ccpack.InitFromBuffer(buf)
if err != nil {
return nil, nil, err
}
if err := ccpack.ValidateCC(ccdata); err != nil {
return nil, nil, err
}
return ccpack.buf, ccpack.depSpec, nil
}
// InitFromFS returns the chaincode and its package from the file system
func (ccpack *CDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath)
}
// PutChaincodeToFS - serializes chaincode to a package on the file system
func (ccpack *CDSPackage) PutChaincodeToFS() error {
if ccpack.buf == nil {
return errors.New("uninitialized package")
}
if ccpack.id == nil {
return errors.New("id cannot be nil if buf is not nil")
}
if ccpack.depSpec == nil {
return errors.New("depspec cannot be nil if buf is not nil")
}
if ccpack.data == nil {
return errors.New("nil data")
}
if ccpack.datab == nil {
return errors.New("nil data bytes")
}
ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
// return error if chaincode exists
path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
if _, err := os.Stat(path); err == nil {
return fmt.Errorf("chaincode %s exists", path)
}
if err := os.WriteFile(path, ccpack.buf, 0o644); err != nil {
return err
}
return nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ccprovider
// LoadPackage loads a chaincode package from the file system
func LoadPackage(ccNameVersion string, path string, getHasher GetHasher) (CCPackage, error) {
return (&CCInfoFSImpl{GetHasher: getHasher}).GetChaincodeFromPath(ccNameVersion, path)
}
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ccprovider
import (
"errors"
"fmt"
"os"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/factory"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
pb "github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/core/common/ccpackage"
"google.golang.org/protobuf/proto"
)
// -------- SignedCDSPackage ---------
// SignedCDSPackage encapsulates SignedChaincodeDeploymentSpec.
type SignedCDSPackage struct {
buf []byte
depSpec *pb.ChaincodeDeploymentSpec
sDepSpec *pb.SignedChaincodeDeploymentSpec
env *common.Envelope
data *SignedCDSData
datab []byte
id []byte
GetHasher GetHasher
}
// GetId gets the fingerprint of the chaincode based on package computation
func (ccpack *SignedCDSPackage) GetId() []byte {
// this has to be after creating a package and initializing it
// If those steps fail, GetId() should never be called
if ccpack.id == nil {
panic("GetId called on uninitialized package")
}
return ccpack.id
}
// GetDepSpec gets the ChaincodeDeploymentSpec from the package
func (ccpack *SignedCDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
// this has to be after creating a package and initializing it
// If those steps fail, GetDepSpec() should never be called
if ccpack.depSpec == nil {
panic("GetDepSpec called on uninitialized package")
}
return ccpack.depSpec
}
// GetInstantiationPolicy gets the instantiation policy from the package
func (ccpack *SignedCDSPackage) GetInstantiationPolicy() []byte {
if ccpack.sDepSpec == nil {
panic("GetInstantiationPolicy called on uninitialized package")
}
return ccpack.sDepSpec.InstantiationPolicy
}
// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
func (ccpack *SignedCDSPackage) GetDepSpecBytes() []byte {
// this has to be after creating a package and initializing it
// If those steps fail, GetDepSpecBytes() should never be called
if ccpack.sDepSpec == nil || ccpack.sDepSpec.ChaincodeDeploymentSpec == nil {
panic("GetDepSpecBytes called on uninitialized package")
}
return ccpack.sDepSpec.ChaincodeDeploymentSpec
}
// GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
func (ccpack *SignedCDSPackage) GetPackageObject() proto.Message {
return ccpack.env
}
// GetChaincodeData gets the ChaincodeData
func (ccpack *SignedCDSPackage) GetChaincodeData() *ChaincodeData {
// this has to be after creating a package and initializing it
// If those steps fail, GetChaincodeData() should never be called
if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil {
panic("GetChaincodeData called on uninitialized package")
}
var instPolicy []byte
if ccpack.sDepSpec != nil {
instPolicy = ccpack.sDepSpec.InstantiationPolicy
}
return &ChaincodeData{
Name: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name,
Version: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version,
Data: ccpack.datab,
Id: ccpack.id,
InstantiationPolicy: instPolicy,
}
}
func (ccpack *SignedCDSPackage) getCDSData(scds *pb.SignedChaincodeDeploymentSpec) ([]byte, []byte, *SignedCDSData, error) {
// check for nil argument. It is an assertion that getCDSData
// is never called on a package that did not go through/succeed
// package initialization.
if scds == nil {
panic("nil cds")
}
cds := &pb.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(scds.ChaincodeDeploymentSpec, cds)
if err != nil {
return nil, nil, nil, err
}
if err = factory.InitFactories(nil); err != nil {
return nil, nil, nil, fmt.Errorf("Internal error, BCCSP could not be initialized : %s", err)
}
// get the hash object
hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{})
if err != nil {
return nil, nil, nil, err
}
scdsdata := &SignedCDSData{}
// get the code hash
hash.Write(cds.CodePackage)
scdsdata.CodeHash = hash.Sum(nil)
hash.Reset()
// get the metadata hash
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
scdsdata.MetaDataHash = hash.Sum(nil)
hash.Reset()
// get the signature hashes
if scds.InstantiationPolicy == nil {
return nil, nil, nil, fmt.Errorf("instantiation policy cannot be nil for chaincode (%s:%s)", cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version)
}
hash.Write(scds.InstantiationPolicy)
for _, o := range scds.OwnerEndorsements {
hash.Write(o.Endorser)
}
scdsdata.SignatureHash = hash.Sum(nil)
// marshall data
b, err := proto.Marshal(scdsdata)
if err != nil {
return nil, nil, nil, err
}
hash.Reset()
// compute the id
hash.Write(scdsdata.CodeHash)
hash.Write(scdsdata.MetaDataHash)
hash.Write(scdsdata.SignatureHash)
id := hash.Sum(nil)
return b, id, scdsdata, nil
}
// ValidateCC returns error if the chaincode is not found or if its not a
// ChaincodeDeploymentSpec
func (ccpack *SignedCDSPackage) ValidateCC(ccdata *ChaincodeData) error {
if ccpack.sDepSpec == nil {
return errors.New("uninitialized package")
}
if ccpack.sDepSpec.ChaincodeDeploymentSpec == nil {
return errors.New("signed chaincode deployment spec cannot be nil in a package")
}
if ccpack.depSpec == nil {
return errors.New("chaincode deployment spec cannot be nil in a package")
}
// This is a hack. LSCC expects a specific LSCC error when names are invalid so it
// has its own validation code. We can't use that error because of import cycles.
// Unfortunately, we also need to check if what have makes some sort of sense as
// protobuf will gladly deserialize garbage and there are paths where we assume that
// a successful unmarshal means everything works but, if it fails, we try to unmarshal
// into something different.
if !isPrintable(ccdata.Name) {
return fmt.Errorf("invalid chaincode name: %q", ccdata.Name)
}
if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
}
otherdata := &SignedCDSData{}
err := proto.Unmarshal(ccdata.Data, otherdata)
if err != nil {
return err
}
if !proto.Equal(ccpack.data, otherdata) {
return errors.New("data mismatch")
}
return nil
}
// InitFromBuffer sets the buffer if valid and returns ChaincodeData
func (ccpack *SignedCDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
env := &common.Envelope{}
err := proto.Unmarshal(buf, env)
if err != nil {
return nil, errors.New("failed to unmarshal envelope from bytes")
}
cHdr, sDepSpec, err := ccpackage.ExtractSignedCCDepSpec(env)
if err != nil {
return nil, err
}
if cHdr.Type != int32(common.HeaderType_CHAINCODE_PACKAGE) {
return nil, errors.New("invalid type of envelope for chaincode package")
}
depSpec := &pb.ChaincodeDeploymentSpec{}
err = proto.Unmarshal(sDepSpec.ChaincodeDeploymentSpec, depSpec)
if err != nil {
return nil, errors.New("error getting deployment spec")
}
databytes, id, data, err := ccpack.getCDSData(sDepSpec)
if err != nil {
return nil, err
}
ccpack.buf = buf
ccpack.sDepSpec = sDepSpec
ccpack.depSpec = depSpec
ccpack.env = env
ccpack.data = data
ccpack.datab = databytes
ccpack.id = id
return ccpack.GetChaincodeData(), nil
}
// InitFromFS returns the chaincode and its package from the file system
func (ccpack *SignedCDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath)
}
// InitFromPath returns the chaincode and its package from the file system
func (ccpack *SignedCDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
buf, err := GetChaincodePackageFromPath(ccNameVersion, path)
if err != nil {
return nil, nil, err
}
if _, err = ccpack.InitFromBuffer(buf); err != nil {
return nil, nil, err
}
return ccpack.buf, ccpack.depSpec, nil
}
// PutChaincodeToFS - serializes chaincode to a package on the file system
func (ccpack *SignedCDSPackage) PutChaincodeToFS() error {
if ccpack.buf == nil {
return errors.New("uninitialized package")
}
if ccpack.id == nil {
return errors.New("id cannot be nil if buf is not nil")
}
if ccpack.sDepSpec == nil || ccpack.depSpec == nil {
return errors.New("depspec cannot be nil if buf is not nil")
}
if ccpack.env == nil {
return errors.New("env cannot be nil if buf and depspec are not nil")
}
if ccpack.data == nil {
return errors.New("nil data")
}
if ccpack.datab == nil {
return errors.New("nil data bytes")
}
ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
// return error if chaincode exists
path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
if _, err := os.Stat(path); err == nil {
return fmt.Errorf("chaincode %s exists", path)
}
if err := os.WriteFile(path, ccpack.buf, 0o644); err != nil {
return err
}
return nil
}
/*
# Copyright State Street Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
*/
package ccprovider
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"strings"
customBytes "github.com/AdamKorcz/bugdetectors/bytes"
)
// The targz metadata provider is reference for other providers (such as what CAR would
// implement). Currently it treats only statedb metadata but will be generalized in future
// to allow for arbitrary metadata to be packaged with the chaincode.
const (
ccPackageStatedbDir = "META-INF/statedb/"
)
type PersistenceAdapter func([]byte) ([]byte, error)
func (pa PersistenceAdapter) GetDBArtifacts(codePackage []byte) ([]byte, error) {
return pa(codePackage)
}
// MetadataAsTarEntries extracts metadata from a chaincode package
func MetadataAsTarEntries(code []byte) ([]byte, error) {
is := bytes.NewReader(code)
gr, err := gzip.NewReader(is)
if err != nil {
ccproviderLogger.Errorf("Failure opening codepackage gzip stream: %s", err)
return nil, err
}
statedbTarBuffer := bytes.NewBuffer(nil)
tw := tar.NewWriter(statedbTarBuffer)
tr := tar.NewReader(gr)
// For each file in the code package tar,
// add it to the statedb artifact tar if it has "statedb" in the path
for {
header, err := tr.Next()
if err == io.EOF {
// We only get here if there are no more entries to scan
break
}
if err != nil {
return nil, err
}
if !strings.HasPrefix(header.Name, ccPackageStatedbDir) {
continue
}
if err = tw.WriteHeader(header); err != nil {
ccproviderLogger.Error("Error adding header to statedb tar:", err, header.Name)
return nil, err
}
if _, err := io.Copy(tw, tr); err != nil {
ccproviderLogger.Error("Error copying file to statedb tar:", err, header.Name)
return nil, err
}
ccproviderLogger.Debug("Wrote file to statedb tar:", header.Name)
}
if err = tw.Close(); err != nil {
return nil, err
}
ccproviderLogger.Debug("Created metadata tar")
return customBytes.CheckLen(statedbTarBuffer.Bytes(), "/src/fabric/core/common/ccprovider/targzmetadataprovider.go:76:9 (May be slightly inaccurate) NEW_LINEstatedbTarBuffer.Bytes()"), nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
"strings"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/protoutil"
)
// Collection defines a common interface for collections
type Collection interface {
// SetTxContext configures the tx-specific ephemeral collection info, such
// as txid, nonce, creator -- for future use
// SetTxContext(parameters ...interface{})
// CollectionID returns this collection's ID
CollectionID() string
// GetEndorsementPolicy returns the endorsement policy for validation -- for
// future use
// GetEndorsementPolicy() string
// MemberOrgs returns the collection's members as MSP IDs. This serves as
// a human-readable way of quickly identifying who is part of a collection.
MemberOrgs() map[string]struct{}
}
// CollectionAccessPolicy encapsulates functions for the access policy of a collection
type CollectionAccessPolicy interface {
// AccessFilter returns a member filter function for a collection
AccessFilter() Filter
// The minimum number of peers private data will be sent to upon
// endorsement. The endorsement would fail if dissemination to at least
// this number of peers is not achieved.
RequiredPeerCount() int
// The maximum number of peers that private data will be sent to
// upon endorsement. This number has to be bigger than RequiredPeerCount().
MaximumPeerCount() int
// MemberOrgs returns the collection's members as MSP IDs. This serves as
// a human-readable way of quickly identifying who is part of a collection.
MemberOrgs() map[string]struct{}
// IsMemberOnlyRead returns a true if only collection members can read
// the private data
IsMemberOnlyRead() bool
// IsMemberOnlyWrite returns a true if only collection members can write
// the private data
IsMemberOnlyWrite() bool
}
// CollectionPersistenceConfigs encapsulates configurations related to persistence of a collection
type CollectionPersistenceConfigs interface {
// BlockToLive returns the number of blocks after which the collection data expires.
// For instance if the value is set to 10, a key last modified by block number 100
// will be purged at block number 111. A zero value is treated same as MaxUint64
BlockToLive() uint64
}
// Filter defines a rule that filters peers according to data signed by them.
// The Identity in the SignedData is a SerializedIdentity of a peer.
// The Data is a message the peer signed, and the Signature is the corresponding
// Signature on that Data.
// Returns: True, if the policy holds for the given signed data.
//
// False otherwise
type Filter func(protoutil.SignedData) bool
// CollectionStore provides various APIs to retrieves stored collections and perform
// membership check & read permission check based on the collection's properties.
// TODO: Refactor CollectionStore - FAB-13082
// (1) function such as RetrieveCollection() and RetrieveCollectionConfigPackage() are
//
// never used except in mocks and test files.
//
// (2) in gossip, at least in 7 different places, the following 3 operations
//
// are repeated which can be avoided by introducing a API called IsAMemberOf().
// (i) retrieves collection access policy by calling RetrieveCollectionAccessPolicy()
// (ii) get the access filter func from the collection access policy
// (iii) create the evaluation policy and check for membership
//
// (3) we would need a cache in collection store to avoid repeated crypto operation.
//
// This would be simple to implement when we introduce IsAMemberOf() APIs.
type CollectionStore interface {
// RetrieveCollection retrieves the collection in the following way:
// If the TxID exists in the ledger, the collection that is returned has the
// latest configuration that was committed into the ledger before this txID
// was committed.
// Else - it's the latest configuration for the collection.
RetrieveCollection(CollectionCriteria) (Collection, error)
// RetrieveCollectionAccessPolicy retrieves a collection's access policy
RetrieveCollectionAccessPolicy(CollectionCriteria) (CollectionAccessPolicy, error)
// RetrieveCollectionConfig retrieves a collection's config
RetrieveCollectionConfig(CollectionCriteria) (*peer.StaticCollectionConfig, error)
// RetrieveCollectionConfigPackage retrieves the whole configuration package
// for the chaincode with the supplied criteria
RetrieveCollectionConfigPackage(CollectionCriteria) (*peer.CollectionConfigPackage, error)
// RetrieveCollectionPersistenceConfigs retrieves the collection's persistence related configurations
RetrieveCollectionPersistenceConfigs(CollectionCriteria) (CollectionPersistenceConfigs, error)
// RetrieveReadWritePermission retrieves the read-write permission of the creator of the
// signedProposal for a given collection using collection access policy and flags such as
// memberOnlyRead & memberOnlyWrite
RetrieveReadWritePermission(CollectionCriteria, *peer.SignedProposal, ledger.QueryExecutor) (bool, bool, error)
CollectionFilter
}
type CollectionFilter interface {
// AccessFilter retrieves the collection's filter that matches a given channel and a collectionPolicyConfig
AccessFilter(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (Filter, error)
}
const (
// Collection-specific constants
// CollectionSeparator is the separator used to build the KVS
// key storing the collections of a chaincode; note that we are
// using as separator a character which is illegal for either the
// name or the version of a chaincode so there cannot be any
// collisions when choosing the name
collectionSeparator = "~"
// collectionSuffix is the suffix of the KVS key storing the
// collections of a chaincode
collectionSuffix = "collection"
)
// BuildCollectionKVSKey constructs the collection config key for a given chaincode name
func BuildCollectionKVSKey(ccname string) string {
return ccname + collectionSeparator + collectionSuffix
}
// IsCollectionConfigKey detects if a key is a collection key
func IsCollectionConfigKey(key string) bool {
return strings.Contains(key, collectionSeparator)
}
// GetCCNameFromCollectionConfigKey returns the chaincode name given a collection config key
func GetCCNameFromCollectionConfigKey(key string) string {
splittedKey := strings.Split(key, collectionSeparator)
return splittedKey[0]
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/core/chaincode/implicitcollection"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
)
var logger = flogging.MustGetLogger("common.privdata")
// MembershipProvider can be used to check whether a peer is eligible to a collection or not
type MembershipProvider struct {
mspID string
selfSignedData protoutil.SignedData
IdentityDeserializerFactory func(chainID string) msp.IdentityDeserializer
myImplicitCollectionName string
}
// NewMembershipInfoProvider returns MembershipProvider
func NewMembershipInfoProvider(mspID string, selfSignedData protoutil.SignedData, identityDeserializerFunc func(chainID string) msp.IdentityDeserializer) *MembershipProvider {
return &MembershipProvider{
mspID: mspID,
selfSignedData: selfSignedData,
IdentityDeserializerFactory: identityDeserializerFunc,
myImplicitCollectionName: implicitcollection.NameForOrg(mspID),
}
}
// AmMemberOf checks whether the current peer is a member of the given collection config.
// It is used when a chaincode is upgraded to see if the peer's org has become eligible after a collection change.
func (m *MembershipProvider) AmMemberOf(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (bool, error) {
deserializer := m.IdentityDeserializerFactory(channelName)
// Do a simple check to see if the mspid matches any principal identities in the SignaturePolicy - FAB-17059
if collectionPolicyConfig.GetSignaturePolicy() == nil {
logger.Warningf("collection membership policy is nil")
return false, nil
}
memberOrgs := getMemberOrgs(collectionPolicyConfig.GetSignaturePolicy().GetIdentities(), deserializer)
_, ok := memberOrgs[m.mspID]
return ok, nil
}
func (m *MembershipProvider) MyImplicitCollectionName() string {
return m.myImplicitCollectionName
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// SimpleCollection implements a collection with static properties
// and a public member set
type SimpleCollection struct {
name string
accessPolicy policies.Policy
memberOrgs map[string]struct{}
conf *peer.StaticCollectionConfig
}
type SimpleCollectionPersistenceConfigs struct {
blockToLive uint64
}
// NewSimpleCollection returns a simple collection object based on a given
// StaticCollectionConfig proto that has all the necessary information
func NewSimpleCollection(collectionConfig *peer.StaticCollectionConfig, deserializer msp.IdentityDeserializer) (*SimpleCollection, error) {
sc := &SimpleCollection{}
err := sc.Setup(collectionConfig, deserializer)
return sc, err
}
// CollectionID returns the collection's ID
func (sc *SimpleCollection) CollectionID() string {
return sc.name
}
// MemberOrgs returns the MSP IDs that are part of this collection
func (sc *SimpleCollection) MemberOrgs() map[string]struct{} {
return sc.memberOrgs
}
// RequiredPeerCount returns the minimum number of peers
// required to send private data to
func (sc *SimpleCollection) RequiredPeerCount() int {
return int(sc.conf.RequiredPeerCount)
}
// MaximumPeerCount returns the maximum number of peers
// to which the private data will be sent
func (sc *SimpleCollection) MaximumPeerCount() int {
return int(sc.conf.MaximumPeerCount)
}
// AccessFilter returns the member filter function that evaluates signed data
// against the member access policy of this collection
func (sc *SimpleCollection) AccessFilter() Filter {
return func(sd protoutil.SignedData) bool {
if err := sc.accessPolicy.EvaluateSignedData([]*protoutil.SignedData{&sd}); err != nil {
return false
}
return true
}
}
// IsMemberOnlyRead returns whether only collection member
// has the read permission
func (sc *SimpleCollection) IsMemberOnlyRead() bool {
return sc.conf.MemberOnlyRead
}
// IsMemberOnlyWrite returns whether only collection member
// has the write permission
func (sc *SimpleCollection) IsMemberOnlyWrite() bool {
return sc.conf.MemberOnlyWrite
}
// Setup configures a simple collection object based on a given
// StaticCollectionConfig proto that has all the necessary information
func (sc *SimpleCollection) Setup(collectionConfig *peer.StaticCollectionConfig, deserializer msp.IdentityDeserializer) error {
if collectionConfig == nil {
return errors.New("Nil config passed to collection setup")
}
sc.conf = proto.Clone(collectionConfig).(*peer.StaticCollectionConfig)
sc.name = collectionConfig.GetName()
// get the access signature policy envelope
collectionPolicyConfig := collectionConfig.GetMemberOrgsPolicy()
if collectionPolicyConfig == nil {
return errors.New("Collection config policy is nil")
}
accessPolicyEnvelope := collectionPolicyConfig.GetSignaturePolicy()
if accessPolicyEnvelope == nil {
return errors.New("Collection config access policy is nil")
}
err := sc.setupAccessPolicy(collectionPolicyConfig, deserializer)
if err != nil {
return err
}
// get member org MSP IDs from the envelope, identities that fail to deserialize will not be returned
sc.memberOrgs = getMemberOrgs(accessPolicyEnvelope.Identities, deserializer)
return nil
}
// setupAccessPolicy configures a simple collection object based on a given
// StaticCollectionConfig proto that has all the necessary information
func (sc *SimpleCollection) setupAccessPolicy(collectionPolicyConfig *peer.CollectionPolicyConfig, deserializer msp.IdentityDeserializer) error {
var err error
sc.accessPolicy, err = getPolicy(collectionPolicyConfig, deserializer)
return err
}
// BlockToLive return collection's block to live configuration
func (s *SimpleCollectionPersistenceConfigs) BlockToLive() uint64 {
return s.blockToLive
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
"fmt"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// State retrieves data from the state.
type State interface {
// GetState retrieves the value for the given key in the given namespace
GetState(namespace string, key string) ([]byte, error)
}
type NoSuchCollectionError CollectionCriteria
func (f NoSuchCollectionError) Error() string {
return fmt.Sprintf("collection %s/%s/%s could not be found", f.Channel, f.Namespace, f.Collection)
}
// A QueryExecutorFactory is responsible for creating ledger.QueryExectuor
// instances.
type QueryExecutorFactory interface {
NewQueryExecutor() (ledger.QueryExecutor, error)
}
// ChaincodeInfoProvider provides information about deployed chaincode.
// LSCC module is expected to provide an implementation for this dependencys
type ChaincodeInfoProvider interface {
// ChaincodeInfo returns the info about a deployed chaincode.
ChaincodeInfo(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error)
// CollectionInfo returns the proto msg that defines the named collection.
// This function can be used for both explicit and implicit collections.
CollectionInfo(channelName, chaincodeName, collectionName string, qe ledger.SimpleQueryExecutor) (*peer.StaticCollectionConfig, error)
// AllCollectionsConfigPkg returns a combined collection config pkg that contains both explicit and implicit collections
AllCollectionsConfigPkg(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*peer.CollectionConfigPackage, error)
}
// IdentityDeserializerFactory creates msp.IdentityDeserializer for
// a chain.
type IdentityDeserializerFactory interface {
GetIdentityDeserializer(chainID string) msp.IdentityDeserializer
}
// IdentityDeserializerFactoryFunc is a function adapater for
// IdentityDeserializerFactory.
type IdentityDeserializerFactoryFunc func(chainID string) msp.IdentityDeserializer
func (i IdentityDeserializerFactoryFunc) GetIdentityDeserializer(chainID string) msp.IdentityDeserializer {
return i(chainID)
}
// CollectionCriteria defines an element of a private data that corresponds
// to a certain transaction and collection
type CollectionCriteria struct {
Channel string
Collection string
Namespace string
}
type SimpleCollectionStore struct {
qeFactory QueryExecutorFactory
ccInfoProvider ChaincodeInfoProvider
idDeserializerFactory IdentityDeserializerFactory
}
func NewSimpleCollectionStore(
qeFactory QueryExecutorFactory,
ccInfoProvider ChaincodeInfoProvider,
idDeserializerFactory IdentityDeserializerFactory,
) *SimpleCollectionStore {
return &SimpleCollectionStore{
qeFactory: qeFactory,
ccInfoProvider: ccInfoProvider,
idDeserializerFactory: idDeserializerFactory,
}
}
func (c *SimpleCollectionStore) retrieveCollectionConfigPackage(cc CollectionCriteria, qe ledger.QueryExecutor) (*peer.CollectionConfigPackage, error) {
var err error
if qe == nil {
qe, err = c.qeFactory.NewQueryExecutor()
if err != nil {
return nil, errors.WithMessagef(err, "could not retrieve query executor for collection criteria %#v", cc)
}
defer qe.Done()
}
return c.ccInfoProvider.AllCollectionsConfigPkg(cc.Channel, cc.Namespace, qe)
}
// RetrieveCollectionConfigPackageFromState retrieves the collection config package from the given key from the given state
func RetrieveCollectionConfigPackageFromState(cc CollectionCriteria, state State) (*peer.CollectionConfigPackage, error) {
cb, err := state.GetState("lscc", BuildCollectionKVSKey(cc.Namespace))
if err != nil {
return nil, errors.WithMessagef(err, "error while retrieving collection for collection criteria %#v", cc)
}
if cb == nil {
return nil, NoSuchCollectionError(cc)
}
conf, err := ParseCollectionConfig(cb)
if err != nil {
return nil, errors.Wrapf(err, "invalid configuration for collection criteria %#v", cc)
}
return conf, nil
}
// ParseCollectionConfig parses the collection configuration from the given serialized representation.
func ParseCollectionConfig(colBytes []byte) (*peer.CollectionConfigPackage, error) {
collections := &peer.CollectionConfigPackage{}
err := proto.Unmarshal(colBytes, collections)
if err != nil {
return nil, errors.WithStack(err)
}
return collections, nil
}
// RetrieveCollectionConfig retrieves a collection's config
func (c *SimpleCollectionStore) RetrieveCollectionConfig(cc CollectionCriteria) (*peer.StaticCollectionConfig, error) {
return c.retrieveCollectionConfig(cc, nil)
}
func (c *SimpleCollectionStore) retrieveCollectionConfig(cc CollectionCriteria, qe ledger.QueryExecutor) (*peer.StaticCollectionConfig, error) {
var err error
if qe == nil {
qe, err = c.qeFactory.NewQueryExecutor()
if err != nil {
return nil, errors.WithMessagef(err, "could not retrieve query executor for collection criteria %#v", cc)
}
defer qe.Done()
}
collConfig, err := c.ccInfoProvider.CollectionInfo(cc.Channel, cc.Namespace, cc.Collection, qe)
if err != nil {
return nil, err
}
if collConfig == nil {
return nil, NoSuchCollectionError(cc)
}
return collConfig, nil
}
func (c *SimpleCollectionStore) retrieveSimpleCollection(cc CollectionCriteria, qe ledger.QueryExecutor) (*SimpleCollection, error) {
staticCollectionConfig, err := c.retrieveCollectionConfig(cc, qe)
if err != nil {
return nil, err
}
sc := &SimpleCollection{}
err = sc.Setup(staticCollectionConfig, c.idDeserializerFactory.GetIdentityDeserializer(cc.Channel))
if err != nil {
return nil, errors.WithMessagef(err, "error setting up collection for collection criteria %#v", cc)
}
return sc, nil
}
func (c *SimpleCollectionStore) AccessFilter(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (Filter, error) {
sc := &SimpleCollection{}
err := sc.setupAccessPolicy(collectionPolicyConfig, c.idDeserializerFactory.GetIdentityDeserializer(channelName))
if err != nil {
return nil, err
}
return sc.AccessFilter(), nil
}
func (c *SimpleCollectionStore) RetrieveCollection(cc CollectionCriteria) (Collection, error) {
return c.retrieveSimpleCollection(cc, nil)
}
func (c *SimpleCollectionStore) RetrieveCollectionAccessPolicy(cc CollectionCriteria) (CollectionAccessPolicy, error) {
return c.retrieveSimpleCollection(cc, nil)
}
func (c *SimpleCollectionStore) RetrieveCollectionConfigPackage(cc CollectionCriteria) (*peer.CollectionConfigPackage, error) {
return c.retrieveCollectionConfigPackage(cc, nil)
}
// RetrieveCollectionPersistenceConfigs retrieves the collection's persistence related configurations
func (c *SimpleCollectionStore) RetrieveCollectionPersistenceConfigs(cc CollectionCriteria) (CollectionPersistenceConfigs, error) {
staticCollectionConfig, err := c.retrieveCollectionConfig(cc, nil)
if err != nil {
return nil, err
}
return &SimpleCollectionPersistenceConfigs{staticCollectionConfig.BlockToLive}, nil
}
// RetrieveReadWritePermission retrieves the read-write permission of the creator of the
// signedProposal for a given collection using collection access policy and flags such as
// memberOnlyRead & memberOnlyWrite
func (c *SimpleCollectionStore) RetrieveReadWritePermission(
cc CollectionCriteria,
signedProposal *peer.SignedProposal,
qe ledger.QueryExecutor,
) (bool, bool, error) {
collection, err := c.retrieveSimpleCollection(cc, qe)
if err != nil {
return false, false, err
}
if canAnyoneReadAndWrite(collection) {
return true, true, nil
}
// all members have read-write permission
if isAMember, err := isCreatorOfProposalAMember(signedProposal, collection); err != nil {
return false, false, err
} else if isAMember {
return true, true, nil
}
return !collection.IsMemberOnlyRead(), !collection.IsMemberOnlyWrite(), nil
}
func canAnyoneReadAndWrite(collection *SimpleCollection) bool {
if !collection.IsMemberOnlyRead() && !collection.IsMemberOnlyWrite() {
return true
}
return false
}
func isCreatorOfProposalAMember(signedProposal *peer.SignedProposal, collection *SimpleCollection) (bool, error) {
signedData, err := getSignedData(signedProposal)
if err != nil {
return false, err
}
accessFilter := collection.AccessFilter()
return accessFilter(signedData), nil
}
func getSignedData(signedProposal *peer.SignedProposal) (protoutil.SignedData, error) {
proposal, err := protoutil.UnmarshalProposal(signedProposal.ProposalBytes)
if err != nil {
return protoutil.SignedData{}, err
}
hdr, err := protoutil.UnmarshalHeader(proposal.Header)
if err != nil {
return protoutil.SignedData{}, err
}
shdr, err := protoutil.UnmarshalSignatureHeader(hdr.SignatureHeader)
if err != nil {
return protoutil.SignedData{}, err
}
return protoutil.SignedData{
Data: signedProposal.ProposalBytes,
Identity: shdr.Creator,
Signature: signedProposal.Signature,
}, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
mspp "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/hyperledger/fabric/common/cauthdsl"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// getPolicy creates a new policy from the policy envelope. It will return an error if the envelope has invalid policy config.
// Some caller (e.g., MembershipProvider.AsMemberOf) may drop the error and treat it as a RejectAll policy.
// In the future, we must revisit the callers if this method will return different types of errors.
func getPolicy(collectionPolicyConfig *peer.CollectionPolicyConfig, deserializer msp.IdentityDeserializer) (policies.Policy, error) {
if collectionPolicyConfig == nil {
return nil, errors.New("collection policy config is nil")
}
accessPolicyEnvelope := collectionPolicyConfig.GetSignaturePolicy()
if accessPolicyEnvelope == nil {
return nil, errors.New("collection config access policy is nil")
}
// create access policy from the envelope
pp := cauthdsl.EnvelopeBasedPolicyProvider{Deserializer: deserializer}
accessPolicy, err := pp.NewPolicy(accessPolicyEnvelope)
if err != nil {
return nil, errors.WithMessage(err, "failed constructing policy object out of collection policy config")
}
return accessPolicy, nil
}
// getMemberOrgs returns a map containing member orgs from a list of MSPPrincipals,
// it will skip identities it fails to process
func getMemberOrgs(identities []*mspp.MSPPrincipal, deserializer msp.IdentityDeserializer) map[string]struct{} {
memberOrgs := map[string]struct{}{}
// get member org MSP IDs from the envelope
for _, principal := range identities {
switch principal.PrincipalClassification {
case mspp.MSPPrincipal_ROLE:
// Principal contains the msp role
mspRole := &mspp.MSPRole{}
err := proto.Unmarshal(principal.Principal, mspRole)
if err == nil {
memberOrgs[mspRole.MspIdentifier] = struct{}{}
}
case mspp.MSPPrincipal_IDENTITY:
principalId, err := deserializer.DeserializeIdentity(principal.Principal)
if err == nil {
memberOrgs[principalId.GetMSPIdentifier()] = struct{}{}
}
case mspp.MSPPrincipal_ORGANIZATION_UNIT:
OU := &mspp.OrganizationUnit{}
err := proto.Unmarshal(principal.Principal, OU)
if err == nil {
memberOrgs[OU.MspIdentifier] = struct{}{}
}
}
}
return memberOrgs
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package ledger
import (
"fmt"
"hash"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/common/metrics"
"github.com/hyperledger/fabric-lib-go/healthz"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset"
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
commonledger "github.com/hyperledger/fabric/common/ledger"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
const (
GoLevelDB = "goleveldb"
CouchDB = "CouchDB"
)
// Initializer encapsulates dependencies for PeerLedgerProvider
type Initializer struct {
StateListeners []StateListener
DeployedChaincodeInfoProvider DeployedChaincodeInfoProvider
MembershipInfoProvider MembershipInfoProvider
ChaincodeLifecycleEventProvider ChaincodeLifecycleEventProvider
MetricsProvider metrics.Provider
HealthCheckRegistry HealthCheckRegistry
Config *Config
CustomTxProcessors map[common.HeaderType]CustomTxProcessor
HashProvider HashProvider
}
// Config is a structure used to configure a ledger provider.
type Config struct {
// RootFSPath is the top-level directory where ledger files are stored.
RootFSPath string
// StateDBConfig holds the configuration parameters for the state database.
StateDBConfig *StateDBConfig
// PrivateDataConfig holds the configuration parameters for the private data store.
PrivateDataConfig *PrivateDataConfig
// HistoryDBConfig holds the configuration parameters for the transaction history database.
HistoryDBConfig *HistoryDBConfig
// SnapshotsConfig holds the configuration parameters for the snapshots.
SnapshotsConfig *SnapshotsConfig
}
// StateDBConfig is a structure used to configure the state parameters for the ledger.
type StateDBConfig struct {
// StateDatabase is the database to use for storing last known state. The
// two supported options are "goleveldb" and "CouchDB" (captured in the constants GoLevelDB and CouchDB respectively).
StateDatabase string
// CouchDB is the configuration for CouchDB. It is used when StateDatabase
// is set to "CouchDB".
CouchDB *CouchDBConfig
}
// CouchDBConfig is a structure used to configure a CouchInstance.
type CouchDBConfig struct {
// Address is the hostname:port of the CouchDB database instance.
Address string
// Username is the username used to authenticate with CouchDB. This username
// must have read and write access permissions.
Username string
// Password is the password for Username.
Password string
// MaxRetries is the maximum number of times to retry CouchDB operations on
// failure.
MaxRetries int
// MaxRetriesOnStartup is the maximum number of times to retry CouchDB operations on
// failure when initializing the ledger.
MaxRetriesOnStartup int
// RequestTimeout is the timeout used for CouchDB operations.
RequestTimeout time.Duration
// InternalQueryLimit is the maximum number of records to return internally
// when querying CouchDB.
InternalQueryLimit int
// MaxBatchUpdateSize is the maximum number of records to included in CouchDB
// bulk update operations.
MaxBatchUpdateSize int
// CreateGlobalChangesDB determines whether or not to create the "_global_changes"
// system database.
CreateGlobalChangesDB bool
// RedoLogPath is the directory where the CouchDB redo log files are stored.
RedoLogPath string
// UserCacheSizeMBs denotes the user specified maximum mega bytes (MB) to be allocated
// for the user state cache (i.e., all chaincodes deployed by the user). Note that
// UserCacheSizeMBs needs to be a multiple of 32 MB. If it is not a multiple of 32 MB,
// the peer would round the size to the next multiple of 32 MB.
UserCacheSizeMBs int
}
// PrivateDataConfig is a structure used to configure a private data storage provider.
type PrivateDataConfig struct {
// BatchesInterval is the minimum duration (milliseconds) between batches
// for converting ineligible missing data entries into eligible entries.
BatchesInterval int
// MaxBatchSize is the maximum size of batches when converting ineligible
// missing data entries into eligible entries.
MaxBatchSize int
// PurgeInterval is the number of blocks to wait until purging expired
// private data entries.
PurgeInterval int
// The missing data entries are classified into three categories:
// (1) eligible prioritized
// (2) eligible deprioritized
// (3) ineligible
// The reconciler would fetch the eligible prioritized missing data
// from other peers. A chance for eligible deprioritized missing data
// would be given after every DeprioritizedDataReconcilerInterval
DeprioritizedDataReconcilerInterval time.Duration
// PurgedKeyAuditLogging specifies whether to log private data keys purged from private data store (INFO level) when explicitly purged via chaincode
PurgedKeyAuditLogging bool
}
// HistoryDBConfig is a structure used to configure the transaction history database.
type HistoryDBConfig struct {
Enabled bool
}
// SnapshotsConfig is a structure used to configure snapshot function
type SnapshotsConfig struct {
// RootDir is the top-level directory for the snapshots.
RootDir string
}
// PeerLedgerProvider provides handle to ledger instances
type PeerLedgerProvider interface {
// CreateFromGenesisBlock creates a new ledger with the given genesis block.
// This function guarantees that the creation of ledger and committing the genesis block would an atomic action
// The channel id retrieved from the genesis block is treated as a ledger id
CreateFromGenesisBlock(genesisBlock *common.Block) (PeerLedger, error)
// CreateFromSnapshot creates a new ledger from a snapshot and returns the ledger and channel id.
// The channel id retrieved from snapshot metadata is treated as a ledger id
CreateFromSnapshot(snapshotDir string) (PeerLedger, string, error)
// Open opens an already created ledger
Open(ledgerID string) (PeerLedger, error)
// Exists tells whether the ledger with given id exists
Exists(ledgerID string) (bool, error)
// List lists the ids of the existing ledgers
List() ([]string, error)
// Close closes the PeerLedgerProvider
Close()
}
// PeerLedger differs from the OrdererLedger in that PeerLedger locally maintain a bitmask
// that tells apart valid transactions from invalid ones
type PeerLedger interface {
commonledger.Ledger
// TxIDExists returns true if the specified txID is already present in one of the already committed blocks.
// This function returns error only if there is an underlying condition that prevents checking for the txID, such as an I/O error.
TxIDExists(txID string) (bool, error)
// GetTransactionByID retrieves a transaction by id
GetTransactionByID(txID string) (*peer.ProcessedTransaction, error)
// GetBlockByHash returns a block given it's hash
GetBlockByHash(blockHash []byte) (*common.Block, error)
// GetBlockByTxID returns a block which contains a transaction
GetBlockByTxID(txID string) (*common.Block, error)
// GetTxValidationCodeByTxID returns transaction validation code and block number in which the transaction was committed
GetTxValidationCodeByTxID(txID string) (peer.TxValidationCode, uint64, error)
// NewTxSimulator gives handle to a transaction simulator.
// A client can obtain more than one 'TxSimulator's for parallel execution.
// Any snapshoting/synchronization should be performed at the implementation level if required
NewTxSimulator(txid string) (TxSimulator, error)
// NewQueryExecutor gives handle to a query executor.
// A client can obtain more than one 'QueryExecutor's for parallel execution.
// Any synchronization should be performed at the implementation level if required
NewQueryExecutor() (QueryExecutor, error)
// NewHistoryQueryExecutor gives handle to a history query executor.
// A client can obtain more than one 'HistoryQueryExecutor's for parallel execution.
// Any synchronization should be performed at the implementation level if required
NewHistoryQueryExecutor() (HistoryQueryExecutor, error)
// GetPvtDataAndBlockByNum returns the block and the corresponding pvt data.
// The pvt data is filtered by the list of 'ns/collections' supplied
// A nil filter does not filter any results and causes retrieving all the pvt data for the given blockNum
GetPvtDataAndBlockByNum(blockNum uint64, filter PvtNsCollFilter) (*BlockAndPvtData, error)
// GetPvtDataByNum returns only the pvt data corresponding to the given block number
// The pvt data is filtered by the list of 'ns/collections' supplied in the filter
// A nil filter does not filter any results and causes retrieving all the pvt data for the given blockNum
GetPvtDataByNum(blockNum uint64, filter PvtNsCollFilter) ([]*TxPvtData, error)
// CommitLegacy commits the block and the corresponding pvt data in an atomic operation following the v14 validation/commit path
// TODO: add a new Commit() path that replaces CommitLegacy() for the validation refactor described in FAB-12221
CommitLegacy(blockAndPvtdata *BlockAndPvtData, commitOpts *CommitOptions) error
// GetConfigHistoryRetriever returns the ConfigHistoryRetriever
GetConfigHistoryRetriever() (ConfigHistoryRetriever, error)
// CommitPvtDataOfOldBlocks commits the private data corresponding to already committed block
// If hashes for some of the private data supplied in this function does not match
// the corresponding hash present in the block, the unmatched private data is not
// committed and instead the mismatch information is returned back
CommitPvtDataOfOldBlocks(reconciledPvtdata []*ReconciledPvtdata, unreconciled MissingPvtDataInfo) ([]*PvtdataHashMismatch, error)
// GetMissingPvtDataTracker return the MissingPvtDataTracker
GetMissingPvtDataTracker() (MissingPvtDataTracker, error)
// DoesPvtDataInfoExist returns true when
// (1) the ledger has pvtdata associated with the given block number (or)
// (2) a few or all pvtdata associated with the given block number is missing but the
// missing info is recorded in the ledger (or)
// (3) the block is committed and does not contain any pvtData.
DoesPvtDataInfoExist(blockNum uint64) (bool, error)
// SubmitSnapshotRequest submits a snapshot request for the specified height.
// The request will be stored in the ledger until the ledger's block height is equal to
// the specified height and the snapshot generation is completed.
// When height is 0, it will generate a snapshot at the current block height.
// It returns an error if the specified height is smaller than the ledger's block height.
SubmitSnapshotRequest(height uint64) error
// CancelSnapshotRequest cancels the previously submitted request.
// It returns an error if such a request does not exist or is under processing.
CancelSnapshotRequest(height uint64) error
// PendingSnapshotRequests returns a list of heights for the pending (or under processing) snapshot requests.
PendingSnapshotRequests() ([]uint64, error)
// CommitNotificationsChannel returns a read-only channel on which ledger sends a `CommitNotification`
// when a block is committed. The CommitNotification contains entries for the transactions from the committed block,
// which are not malformed, carry a legitimate TxID, and in addition, are not marked as a duplicate transaction.
// The consumer can close the 'done' channel to signal that the notifications are no longer needed. This will cause the
// CommitNotifications channel to close. There is expected to be only one consumer at a time. The function returns error
// if already a CommitNotification channel is active.
CommitNotificationsChannel(done <-chan struct{}) (<-chan *CommitNotification, error)
}
// SimpleQueryExecutor encapsulates basic functions
type SimpleQueryExecutor interface {
// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
GetState(namespace string, key string) ([]byte, error)
// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key
// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey
// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons.
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
GetStateRangeScanIterator(namespace string, startKey string, endKey string) (commonledger.ResultsIterator, error)
// GetPrivateDataHash gets the hash of the value of a private data item identified by a tuple <namespace, collection, key>
// Function `GetPrivateData` is only meaningful when it is invoked on a peer that is authorized to have the private data
// for the collection <namespace, collection>. However, the function `GetPrivateDataHash` can be invoked on any peer
// to get the hash of the current value
GetPrivateDataHash(namespace, collection, key string) ([]byte, error)
}
// QueryExecutor executes the queries
// Get* methods are for supporting KV-based data model. ExecuteQuery method is for supporting a rich datamodel and query support
//
// ExecuteQuery method in the case of a rich data model is expected to support queries on
// latest state, historical state and on the intersection of state and transactions
type QueryExecutor interface {
SimpleQueryExecutor
// GetStateMetadata returns the metadata for given namespace and key
GetStateMetadata(namespace, key string) (map[string][]byte, error)
// GetStateMultipleKeys gets the values for multiple keys in a single call
GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error)
// GetStateRangeScanIteratorWithPagination returns an iterator that contains all the key-values between given key ranges.
// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key
// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey
// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons.
// The page size parameter limits the number of returned results.
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
GetStateRangeScanIteratorWithPagination(namespace string, startKey, endKey string, pageSize int32) (QueryResultsIterator, error)
// ExecuteQuery executes the given query and returns an iterator that contains results of type specific to the underlying data store.
// Only used for state databases that support query
// For a chaincode, the namespace corresponds to the chaincodeId
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
ExecuteQuery(namespace, query string) (commonledger.ResultsIterator, error)
// ExecuteQueryWithPagination executes the given query and returns an iterator that contains results of type specific to the underlying data store.
// The bookmark and page size parameters are associated with the pagination.
// Only used for state databases that support query
// For a chaincode, the namespace corresponds to the chaincodeId
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (QueryResultsIterator, error)
// GetPrivateData gets the value of a private data item identified by a tuple <namespace, collection, key>
GetPrivateData(namespace, collection, key string) ([]byte, error)
// GetPrivateDataMetadata gets the metadata of a private data item identified by a tuple <namespace, collection, key>
GetPrivateDataMetadata(namespace, collection, key string) (map[string][]byte, error)
// GetPrivateDataMetadataByHash gets the metadata of a private data item identified by a tuple <namespace, collection, keyhash>
GetPrivateDataMetadataByHash(namespace, collection string, keyhash []byte) (map[string][]byte, error)
// GetPrivateDataMultipleKeys gets the values for the multiple private data items in a single call
GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([][]byte, error)
// GetPrivateDataRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key
// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey
// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons.
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error)
// ExecuteQueryOnPrivateData executes the given query and returns an iterator that contains results of type specific to the underlying data store.
// Only used for state databases that support query
// For a chaincode, the namespace corresponds to the chaincodeId
// The returned ResultsIterator contains results of type *KV which is defined in fabric-protos/ledger/queryresult.
ExecuteQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error)
// Done releases resources occupied by the QueryExecutor
Done()
}
// HistoryQueryExecutor executes the history queries
type HistoryQueryExecutor interface {
// GetHistoryForKey retrieves the history of values for a key.
// The returned ResultsIterator contains results of type *KeyModification which is defined in fabric-protos/ledger/queryresult.
GetHistoryForKey(namespace string, key string) (commonledger.ResultsIterator, error)
}
// TxSimulator simulates a transaction on a consistent snapshot of the 'as recent state as possible'
// Set* methods are for supporting KV-based data model. ExecuteUpdate method is for supporting a rich datamodel and query support
type TxSimulator interface {
QueryExecutor
// SetState sets the given value for the given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
SetState(namespace string, key string, value []byte) error
// DeleteState deletes the given namespace and key
DeleteState(namespace string, key string) error
// SetStateMultipleKeys sets the values for multiple keys in a single call
SetStateMultipleKeys(namespace string, kvs map[string][]byte) error
// SetStateMetadata sets the metadata associated with an existing key-tuple <namespace, key>
SetStateMetadata(namespace, key string, metadata map[string][]byte) error
// DeleteStateMetadata deletes the metadata (if any) associated with an existing key-tuple <namespace, key>
DeleteStateMetadata(namespace, key string) error
// ExecuteUpdate for supporting rich data model (see comments on QueryExecutor above)
ExecuteUpdate(query string) error
// SetPrivateData sets the given value to a key in the private data state represented by the tuple <namespace, collection, key>
SetPrivateData(namespace, collection, key string, value []byte) error
// SetPrivateDataMultipleKeys sets the values for multiple keys in the private data space in a single call
SetPrivateDataMultipleKeys(namespace, collection string, kvs map[string][]byte) error
// DeletePrivateData deletes the given tuple <namespace, collection, key> from private data
DeletePrivateData(namespace, collection, key string) error
// PurgePrivateData purges the given tuple <namespace, collection, key> from private data
PurgePrivateData(namespace, collection, key string) error
// SetPrivateDataMetadata sets the metadata associated with an existing key-tuple <namespace, collection, key>
SetPrivateDataMetadata(namespace, collection, key string, metadata map[string][]byte) error
// DeletePrivateDataMetadata deletes the metadata associated with an existing key-tuple <namespace, collection, key>
DeletePrivateDataMetadata(namespace, collection, key string) error
// GetTxSimulationResults encapsulates the results of the transaction simulation.
// This should contain enough detail for
// - The update in the state that would be caused if the transaction is to be committed
// - The environment in which the transaction is executed so as to be able to decide the validity of the environment
// (at a later time on a different peer) during committing the transactions
// Different ledger implementation (or configurations of a single implementation) may want to represent the above two pieces
// of information in different way in order to support different data-models or optimize the information representations.
// Returned type 'TxSimulationResults' contains the simulation results for both the public data and the private data.
// The public data simulation results are expected to be used as in V1 while the private data simulation results are expected
// to be used by the gossip to disseminate this to the other endorsers (in phase-2 of sidedb)
GetTxSimulationResults() (*TxSimulationResults, error)
}
// QueryResultsIterator - an iterator for query result set
type QueryResultsIterator interface {
commonledger.ResultsIterator
// GetBookmarkAndClose returns a paging bookmark and releases resources occupied by the iterator
GetBookmarkAndClose() string
}
// TxPvtData encapsulates the transaction number and pvt write-set for a transaction
type TxPvtData struct {
SeqInBlock uint64
WriteSet *rwset.TxPvtReadWriteSet
}
// TxPvtDataMap is a map from txNum to the pvtData
type TxPvtDataMap map[uint64]*TxPvtData
// MissingPvtData contains a namespace and collection for
// which the pvtData is not present. It also denotes
// whether the missing pvtData is eligible (i.e., whether
// the peer is member of the [namespace, collection]
type MissingPvtData struct {
Namespace string
Collection string
IsEligible bool
}
// TxMissingPvtData is a map from txNum to the list of
// missing pvtData
type TxMissingPvtData map[uint64][]*MissingPvtData
// BlockAndPvtData encapsulates the block and a map that contains the tuples <seqInBlock, *TxPvtData>
// The map is expected to contain the entries only for the transactions that has associated pvt data
type BlockAndPvtData struct {
Block *common.Block
PvtData TxPvtDataMap
MissingPvtData TxMissingPvtData
}
// ReconciledPvtdata contains the private data for a block for reconciliation
type ReconciledPvtdata struct {
BlockNum uint64
WriteSets TxPvtDataMap
}
// Add adds a given missing private data in the MissingPrivateDataList
func (txMissingPvtData TxMissingPvtData) Add(txNum uint64, ns, coll string, isEligible bool) {
txMissingPvtData[txNum] = append(txMissingPvtData[txNum], &MissingPvtData{ns, coll, isEligible})
}
// RetrievedPvtdata is a dependency that is implemented by coordinator/gossip for ledger
// to be able to purge the transactions from the block after retrieving private data
type RetrievedPvtdata interface {
GetBlockPvtdata() *BlockPvtdata
Purge()
}
// TxPvtdataInfo captures information about the requested private data to be retrieved
type TxPvtdataInfo struct {
TxID string
Invalid bool
SeqInBlock uint64
CollectionPvtdataInfo []*CollectionPvtdataInfo
}
// CollectionPvtdataInfo contains information about the private data for a given collection
type CollectionPvtdataInfo struct {
Namespace, Collection string
ExpectedHash []byte
CollectionConfig *peer.StaticCollectionConfig
Endorsers []*peer.Endorsement
}
// BlockPvtdata contains the retrieved private data as well as missing and ineligible
// private data for use at commit time
type BlockPvtdata struct {
PvtData TxPvtDataMap
MissingPvtData TxMissingPvtData
}
// CommitOptions encapsulates options associated with a block commit.
type CommitOptions struct {
FetchPvtDataFromLedger bool
}
// PvtCollFilter represents the set of the collection names (as keys of the map with value 'true')
type PvtCollFilter map[string]bool
// PvtNsCollFilter specifies the tuple <namespace, PvtCollFilter>
type PvtNsCollFilter map[string]PvtCollFilter
// NewPvtNsCollFilter constructs an empty PvtNsCollFilter
func NewPvtNsCollFilter() PvtNsCollFilter {
return make(map[string]PvtCollFilter)
}
// Has returns true if the pvtdata includes the data for collection <ns,coll>
func (pvtdata *TxPvtData) Has(ns string, coll string) bool {
if pvtdata.WriteSet == nil {
return false
}
for _, nsdata := range pvtdata.WriteSet.NsPvtRwset {
if nsdata.Namespace == ns {
for _, colldata := range nsdata.CollectionPvtRwset {
if colldata.CollectionName == coll {
return true
}
}
}
}
return false
}
// Add adds a namespace-collection tuple to the filter
func (filter PvtNsCollFilter) Add(ns string, coll string) {
collFilter, ok := filter[ns]
if !ok {
collFilter = make(map[string]bool)
filter[ns] = collFilter
}
collFilter[coll] = true
}
// Has returns true if the filter has the entry for tuple namespace-collection
func (filter PvtNsCollFilter) Has(ns string, coll string) bool {
collFilter, ok := filter[ns]
if !ok {
return false
}
return collFilter[coll]
}
// PrivateReads captures which private data collections are read during TX simulation.
type PrivateReads map[string]map[string]struct{}
// Add a collection to the set of private data collections that are read by the chaincode.
func (pr PrivateReads) Add(ns, coll string) {
if _, ok := pr[ns]; !ok {
pr[ns] = map[string]struct{}{}
}
pr[ns][coll] = struct{}{}
}
// Clone returns a copy of this struct.
func (pr PrivateReads) Clone() PrivateReads {
clone := PrivateReads{}
for ns, v := range pr {
for coll := range v {
clone.Add(ns, coll)
}
}
return clone
}
// Exists returns whether a collection has been read
func (pr PrivateReads) Exists(ns, coll string) bool {
if c, ok := pr[ns]; ok {
if _, ok2 := c[coll]; ok2 {
return true
}
}
return false
}
// WritesetMetadata represents the content of the state metadata for each state (key) that gets written to during transaction simulation.
type WritesetMetadata map[string]map[string]map[string]map[string][]byte
// Add metadata to the structure.
func (wm WritesetMetadata) Add(ns, coll, key string, metadata map[string][]byte) {
if _, ok := wm[ns]; !ok {
wm[ns] = map[string]map[string]map[string][]byte{}
}
if _, ok := wm[ns][coll]; !ok {
wm[ns][coll] = map[string]map[string][]byte{}
}
if metadata == nil {
metadata = map[string][]byte{}
}
wm[ns][coll][key] = metadata
}
// Clone returns a copy of this struct.
func (wm WritesetMetadata) Clone() WritesetMetadata {
clone := WritesetMetadata{}
for ns, cm := range wm {
for coll, km := range cm {
for key, metadata := range km {
clone.Add(ns, coll, key, metadata)
}
}
}
return clone
}
// TxSimulationResults captures the details of the simulation results
type TxSimulationResults struct {
PubSimulationResults *rwset.TxReadWriteSet
PvtSimulationResults *rwset.TxPvtReadWriteSet
PrivateReads PrivateReads
WritesetMetadata WritesetMetadata
}
// GetPubSimulationBytes returns the serialized bytes of public readwrite set
func (txSim *TxSimulationResults) GetPubSimulationBytes() ([]byte, error) {
if txSim.PubSimulationResults == nil {
return nil, errors.New("proto: Marshal called with nil")
}
return proto.Marshal(txSim.PubSimulationResults)
}
// GetPvtSimulationBytes returns the serialized bytes of private readwrite set
func (txSim *TxSimulationResults) GetPvtSimulationBytes() ([]byte, error) {
if !txSim.ContainsPvtWrites() {
return nil, nil
}
if txSim.PvtSimulationResults == nil {
return nil, errors.New("proto: Marshal called with nil")
}
return proto.Marshal(txSim.PvtSimulationResults)
}
// ContainsPvtWrites returns true if the simulation results include the private writes
func (txSim *TxSimulationResults) ContainsPvtWrites() bool {
return txSim.PvtSimulationResults != nil
}
// StateListener allows a custom code for performing additional stuff upon state change
// for a particular namespace against which the listener is registered.
// This helps to perform custom tasks other than the state updates.
// A ledger implementation is expected to invoke Function `HandleStateUpdates` once per block and
// the `stateUpdates` parameter passed to the function captures the state changes caused by the block
// for the namespace. The actual data type of stateUpdates depends on the data model enabled.
// For instance, for KV data model, the actual type would be proto message
// `github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset.KVWrite`
// Function `HandleStateUpdates` is expected to be invoked before block is committed and if this
// function returns an error, the ledger implementation is expected to halt block commit operation
// and result in a panic.
// The function Initialize is invoked only once at the time of opening the ledger.
type StateListener interface {
Name() string
Initialize(ledgerID string, qe SimpleQueryExecutor) error
InterestedInNamespaces() []string
HandleStateUpdates(trigger *StateUpdateTrigger) error
StateCommitDone(channelID string)
}
// StateUpdateTrigger encapsulates the information and helper tools that may be used by a StateListener
type StateUpdateTrigger struct {
LedgerID string
StateUpdates StateUpdates
CommittingBlockNum uint64
CommittedStateQueryExecutor SimpleQueryExecutor
PostCommitQueryExecutor SimpleQueryExecutor
}
// StateUpdates encapsulates the state updates
type StateUpdates map[string]*KVStateUpdates
// KVStateUpdates captures the state updates for a namespace for KV datamodel
type KVStateUpdates struct {
PublicUpdates []*kvrwset.KVWrite
CollHashUpdates map[string][]*kvrwset.KVWriteHash
}
// ConfigHistoryRetriever allow retrieving history of collection configs
type ConfigHistoryRetriever interface {
MostRecentCollectionConfigBelow(blockNum uint64, chaincodeName string) (*CollectionConfigInfo, error)
}
// MissingPvtDataTracker allows getting information about the private data that is not missing on the peer
type MissingPvtDataTracker interface {
GetMissingPvtDataInfoForMostRecentBlocks(maxBlocks int) (MissingPvtDataInfo, error)
}
// MissingPvtDataInfo is a map of block number to MissingBlockPvtdataInfo
type MissingPvtDataInfo map[uint64]MissingBlockPvtdataInfo
// MissingBlockPvtdataInfo is a map of transaction number (within the block) to MissingCollectionPvtDataInfo
type MissingBlockPvtdataInfo map[uint64][]*MissingCollectionPvtDataInfo
// MissingCollectionPvtDataInfo includes the name of the chaincode and collection for which private data is missing
type MissingCollectionPvtDataInfo struct {
Namespace, Collection string
}
// CollectionConfigInfo encapsulates a collection config for a chaincode and its committing block number
type CollectionConfigInfo struct {
CollectionConfig *peer.CollectionConfigPackage
CommittingBlockNum uint64
}
// Add adds a missing data entry to the MissingPvtDataInfo Map
func (missingPvtDataInfo MissingPvtDataInfo) Add(blkNum, txNum uint64, ns, coll string) {
missingBlockPvtDataInfo, ok := missingPvtDataInfo[blkNum]
if !ok {
missingBlockPvtDataInfo = make(MissingBlockPvtdataInfo)
missingPvtDataInfo[blkNum] = missingBlockPvtDataInfo
}
if _, ok := missingBlockPvtDataInfo[txNum]; !ok {
missingBlockPvtDataInfo[txNum] = []*MissingCollectionPvtDataInfo{}
}
missingBlockPvtDataInfo[txNum] = append(missingBlockPvtDataInfo[txNum],
&MissingCollectionPvtDataInfo{
Namespace: ns,
Collection: coll,
})
}
// CollConfigNotDefinedError is returned whenever an operation
// is requested on a collection whose config has not been defined
type CollConfigNotDefinedError struct {
Ns string
}
func (e *CollConfigNotDefinedError) Error() string {
return fmt.Sprintf("collection config not defined for chaincode [%s], pass the collection configuration upon chaincode definition/instantiation", e.Ns)
}
// InvalidCollNameError is returned whenever an operation
// is requested on a collection whose name is invalid
type InvalidCollNameError struct {
Ns, Coll string
}
func (e *InvalidCollNameError) Error() string {
return fmt.Sprintf("collection [%s] not defined in the collection config for chaincode [%s]", e.Coll, e.Ns)
}
// PvtdataHashMismatch is used when the hash of private write-set
// does not match the corresponding hash present in the block
// or there is a mismatch with the boot-KV-hashes present in the
// private block store if the legder is created from a snapshot
type PvtdataHashMismatch struct {
BlockNum, TxNum uint64
Namespace, Collection string
}
// DeployedChaincodeInfoProvider is a dependency that is used by ledger to build collection config history
// LSCC module is expected to provide an implementation for this dependencies
type DeployedChaincodeInfoProvider interface {
// Namespaces returns the slice of the namespaces that are used for maintaining chaincode lifecycle data
Namespaces() []string
// UpdatedChaincodes returns the chaincodes that are getting updated by the supplied 'stateUpdates'
UpdatedChaincodes(stateUpdates map[string][]*kvrwset.KVWrite) ([]*ChaincodeLifecycleInfo, error)
// ChaincodeInfo returns the info about a deployed chaincode
ChaincodeInfo(channelName, chaincodeName string, qe SimpleQueryExecutor) (*DeployedChaincodeInfo, error)
// AllChaincodesInfo returns the mapping of chaincode name to DeployedChaincodeInfo for all the deployed chaincodes
AllChaincodesInfo(channelName string, qe SimpleQueryExecutor) (map[string]*DeployedChaincodeInfo, error)
// CollectionInfo returns the proto msg that defines the named collection. This function can be called for both explicit and implicit collections
CollectionInfo(channelName, chaincodeName, collectionName string, qe SimpleQueryExecutor) (*peer.StaticCollectionConfig, error)
// ImplicitCollections returns a slice that contains one proto msg for each of the implicit collections
ImplicitCollections(channelName, chaincodeName string, qe SimpleQueryExecutor) ([]*peer.StaticCollectionConfig, error)
// GenerateImplicitCollectionForOrg generates implicit collection for the org
GenerateImplicitCollectionForOrg(mspid string) *peer.StaticCollectionConfig
// AllCollectionsConfigPkg returns a combined collection config pkg that contains both explicit and implicit collections
AllCollectionsConfigPkg(channelName, chaincodeName string, qe SimpleQueryExecutor) (*peer.CollectionConfigPackage, error)
}
// DeployedChaincodeInfo encapsulates chaincode information from the deployed chaincodes
type DeployedChaincodeInfo struct {
Name string
Hash []byte
Version string
ExplicitCollectionConfigPkg *peer.CollectionConfigPackage
IsLegacy bool
}
// ChaincodeLifecycleInfo captures the update info of a chaincode
type ChaincodeLifecycleInfo struct {
Name string
Deleted bool
Details *ChaincodeLifecycleDetails // Can contain finer details about lifecycle event that can be used for certain optimization
}
// ChaincodeLifecycleDetails captures the finer details of chaincode lifecycle event
type ChaincodeLifecycleDetails struct {
Updated bool // true, if an existing chaincode is updated (false for newly deployed chaincodes).
// Following attributes are meaningful only if 'Updated' is true
HashChanged bool // true, if the chaincode code package is changed
CollectionsUpdated []string // names of the explicit collections that are either added or updated
CollectionsRemoved []string // names of the explicit collections that are removed
}
// MembershipInfoProvider is a dependency that is used by ledger to determine whether the current peer is
// a member of a collection. Gossip module is expected to provide the dependency to ledger
type MembershipInfoProvider interface {
// AmMemberOf checks whether the current peer is a member of the given collection
AmMemberOf(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (bool, error)
// MyImplicitCollectionName returns the name of the implicit collection for the current peer
MyImplicitCollectionName() string
}
type HealthCheckRegistry interface {
RegisterChecker(string, healthz.HealthChecker) error
}
// ChaincodeLifecycleEventListener interface enables ledger components (mainly, intended for statedb)
// to be able to listen to chaincode lifecycle events. 'dbArtifactsTar' represents db specific artifacts
// (such as index specs) packaged in a tar. Note that this interface is redefined here (in addition to
// the one defined in ledger/cceventmgmt package). Using the same interface for the new lifecycle path causes
// a cyclic import dependency. Moreover, eventually the whole package ledger/cceventmgmt is intended to
// be removed when migration to new lifecycle is mandated.
type ChaincodeLifecycleEventListener interface {
// HandleChaincodeDeploy is invoked when chaincode installed + defined becomes true.
// The expected usage are to creates all the necessary statedb structures (such as indexes) and update
// service discovery info. This function is invoked immediately before the committing the state changes
// that contain chaincode definition or when a chaincode install happens
HandleChaincodeDeploy(chaincodeDefinition *ChaincodeDefinition, dbArtifactsTar []byte) error
// ChaincodeDeployDone is invoked after the chaincode deployment is finished - `succeeded` indicates
// whether the deploy finished successfully
ChaincodeDeployDone(succeeded bool)
}
// ChaincodeDefinition captures the info about chaincode
type ChaincodeDefinition struct {
Name string
Hash []byte
Version string
CollectionConfigs *peer.CollectionConfigPackage
}
func (cdef *ChaincodeDefinition) String() string {
return fmt.Sprintf("Name=%s, Version=%s, Hash=%#v", cdef.Name, cdef.Version, cdef.Hash)
}
// ChaincodeLifecycleEventProvider enables ledger to create indexes in the statedb
type ChaincodeLifecycleEventProvider interface {
// RegisterListener is used by ledger to receive a callback alongwith dbArtifacts when a chaincode becomes invocable on the peer
// In addition, if needsExistingChaincodesDefinitions is true, the provider calls back the listener with existing invocable chaincodes
// This parameter is used when we create a ledger from a snapshot so that we can create indexes for the existing invocable chaincodes
// already defined in the imported ledger data
RegisterListener(channelID string, listener ChaincodeLifecycleEventListener, needsExistingChaincodesDefinitions bool) error
}
// CustomTxProcessor allows to generate simulation results during commit time for custom transactions.
// A custom processor may represent the information in a propriety fashion and can use this process to translate
// the information into the form of `TxSimulationResults`. Because, the original information is signed in a
// custom representation, an implementation of a `Processor` should be cautious that the custom representation
// is used for simulation in an deterministic fashion and should take care of compatibility cross fabric versions.
// 'initializingLedger' true indicates that either the transaction being processed is from the genesis block or the ledger is
// syncing the state (which could happen during peer startup if the statedb is found to be lagging behind the blockchain).
// In the former case, the transactions processed are expected to be valid and in the latter case, only valid transactions
// are reprocessed and hence any validation can be skipped.
type CustomTxProcessor interface {
GenerateSimulationResults(txEnvelop *common.Envelope, simulator TxSimulator, initializingLedger bool) error
}
// InvalidTxError is expected to be thrown by a custom transaction processor
// if it wants the ledger to record a particular transaction as invalid
type InvalidTxError struct {
Msg string
}
func (e *InvalidTxError) Error() string {
return e.Msg
}
// HashProvider provides access to a hash.Hash for ledger components.
// Currently works at a stepping stone to decrease surface area of bccsp
type HashProvider interface {
GetHash(opts bccsp.HashOpts) (hash.Hash, error)
}
// CommitNotification is sent on each block commit to the channel returned by PeerLedger.CommitNotificationsChannel().
// TxsInfo field contains the info about individual transactions in the block in the order the transactions appear in the block
// The transactions with a unique and non-empty txID are included in the notification
type CommitNotification struct {
BlockNumber uint64
TxsInfo []*CommitNotificationTxInfo
}
// CommitNotificationTxInfo contains the details of a transaction that is included in the CommitNotification
// ChaincodeID will be nil if the transaction is not an endorser transaction. This may or may not be nil if the transaction is invalid.
// Specifically, it will be nil if the transaction is marked invalid by the validator (e.g., bad payload or insufficient endorements) and it will be non-nil if the transaction is marked invalid for concurrency conflicts.
// However, it is guaranteed be non-nil if the transaction is a valid endorser transaction.
type CommitNotificationTxInfo struct {
TxType common.HeaderType
TxID string
ValidationCode peer.TxValidationCode
ChaincodeID *peer.ChaincodeID
ChaincodeEventData []byte
}
//go:generate counterfeiter -o mock/state_listener.go -fake-name StateListener . StateListener
//go:generate counterfeiter -o mock/query_executor.go -fake-name QueryExecutor . QueryExecutor
//go:generate counterfeiter -o mock/tx_simulator.go -fake-name TxSimulator . TxSimulator
//go:generate counterfeiter -o mock/deployed_ccinfo_provider.go -fake-name DeployedChaincodeInfoProvider . DeployedChaincodeInfoProvider
//go:generate counterfeiter -o mock/membership_info_provider.go -fake-name MembershipInfoProvider . MembershipInfoProvider
//go:generate counterfeiter -o mock/health_check_registry.go -fake-name HealthCheckRegistry . HealthCheckRegistry
//go:generate counterfeiter -o mock/cc_event_listener.go -fake-name ChaincodeLifecycleEventListener . ChaincodeLifecycleEventListener
//go:generate counterfeiter -o mock/custom_tx_processor.go -fake-name CustomTxProcessor . CustomTxProcessor
//go:generate counterfeiter -o mock/cc_event_provider.go -fake-name ChaincodeLifecycleEventProvider . ChaincodeLifecycleEventProvider
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package msp
import (
"bytes"
"crypto/ecdsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp/utils"
"github.com/pkg/errors"
)
type validity struct {
NotBefore, NotAfter time.Time
}
type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
type certificate struct {
Raw asn1.RawContent
TBSCertificate tbsCertificate
SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString
}
type tbsCertificate struct {
Raw asn1.RawContent
Version int `asn1:"optional,explicit,default:0,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer asn1.RawValue
Validity validity
Subject asn1.RawValue
PublicKey publicKeyInfo
UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
}
func isECDSASignedCert(cert *x509.Certificate) bool {
return cert.SignatureAlgorithm == x509.ECDSAWithSHA1 ||
cert.SignatureAlgorithm == x509.ECDSAWithSHA256 ||
cert.SignatureAlgorithm == x509.ECDSAWithSHA384 ||
cert.SignatureAlgorithm == x509.ECDSAWithSHA512
}
// sanitizeECDSASignedCert checks that the signatures signing a cert
// is in low-S. This is checked against the public key of parentCert.
// If the signature is not in low-S, then a new certificate is generated
// that is equals to cert but the signature that is in low-S.
func sanitizeECDSASignedCert(cert *x509.Certificate, parentCert *x509.Certificate) (*x509.Certificate, error) {
if cert == nil {
return nil, errors.New("certificate must be different from nil")
}
if parentCert == nil {
return nil, errors.New("parent certificate must be different from nil")
}
expectedSig, err := utils.SignatureToLowS(parentCert.PublicKey.(*ecdsa.PublicKey), cert.Signature)
if err != nil {
return nil, err
}
// if sig == cert.Signature, nothing needs to be done
if bytes.Equal(cert.Signature, expectedSig) {
return cert, nil
}
// otherwise create a new certificate with the new signature
// 1. Unmarshal cert.Raw to get an instance of certificate,
// the lower level interface that represent an x509 certificate
// encoding
var newCert certificate
newCert, err = certFromX509Cert(cert)
if err != nil {
return nil, err
}
// 2. Change the signature
newCert.SignatureValue = asn1.BitString{Bytes: expectedSig, BitLength: len(expectedSig) * 8}
newCert.Raw = nil
// 3. marshal again newCert. Raw must be nil
newRaw, err := asn1.Marshal(newCert)
if err != nil {
return nil, errors.Wrap(err, "marshalling of the certificate failed")
}
// 4. parse newRaw to get an x509 certificate
return x509.ParseCertificate(newRaw)
}
func certFromX509Cert(cert *x509.Certificate) (certificate, error) {
var newCert certificate
_, err := asn1.Unmarshal(cert.Raw, &newCert)
if err != nil {
return certificate{}, errors.Wrap(err, "unmarshalling of the certificate failed")
}
return newCert, nil
}
// String returns a PEM representation of a certificate
func (c certificate) String() string {
b, err := asn1.Marshal(c)
if err != nil {
return fmt.Sprintf("Failed marshaling cert: %v", err)
}
block := &pem.Block{
Bytes: b,
Type: "CERTIFICATE",
}
b = pem.EncodeToMemory(block)
return string(b)
}
// certToPEM converts the given x509.Certificate to a PEM
// encoded string
func certToPEM(certificate *x509.Certificate) string {
cert, err := certFromX509Cert(certificate)
if err != nil {
mspIdentityLogger.Warning("Failed converting certificate to asn1", err)
return ""
}
return cert.String()
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"encoding/pem"
"os"
"path/filepath"
"github.com/IBM/idemix"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/factory"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"gopkg.in/yaml.v3"
)
// OrganizationalUnitIdentifiersConfiguration is used to represent an OU
// and an associated trusted certificate
type OrganizationalUnitIdentifiersConfiguration struct {
// Certificate is the path to a root or intermediate certificate
Certificate string `yaml:"Certificate,omitempty"`
// OrganizationalUnitIdentifier is the name of the OU
OrganizationalUnitIdentifier string `yaml:"OrganizationalUnitIdentifier,omitempty"`
}
// NodeOUs contains information on how to tell apart clients, peers and orderers
// based on OUs. If the check is enforced, by setting Enabled to true,
// the MSP will consider an identity valid if it is an identity of a client, a peer or
// an orderer. An identity should have only one of these special OUs.
type NodeOUs struct {
// Enable activates the OU enforcement
Enable bool `yaml:"Enable,omitempty"`
// ClientOUIdentifier specifies how to recognize clients by OU
ClientOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"ClientOUIdentifier,omitempty"`
// PeerOUIdentifier specifies how to recognize peers by OU
PeerOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"PeerOUIdentifier,omitempty"`
// AdminOUIdentifier specifies how to recognize admins by OU
AdminOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"AdminOUIdentifier,omitempty"`
// OrdererOUIdentifier specifies how to recognize admins by OU
OrdererOUIdentifier *OrganizationalUnitIdentifiersConfiguration `yaml:"OrdererOUIdentifier,omitempty"`
}
// Configuration represents the accessory configuration an MSP can be equipped with.
// By default, this configuration is stored in a yaml file
type Configuration struct {
// OrganizationalUnitIdentifiers is a list of OUs. If this is set, the MSP
// will consider an identity valid only it contains at least one of these OUs
OrganizationalUnitIdentifiers []*OrganizationalUnitIdentifiersConfiguration `yaml:"OrganizationalUnitIdentifiers,omitempty"`
// NodeOUs enables the MSP to tell apart clients, peers and orderers based
// on the identity's OU.
NodeOUs *NodeOUs `yaml:"NodeOUs,omitempty"`
}
func readFile(file string) ([]byte, error) {
fileCont, err := os.ReadFile(file)
if err != nil {
return nil, errors.Wrapf(err, "could not read file %s", file)
}
return fileCont, nil
}
func readPemFile(file string) ([]byte, error) {
bytes, err := readFile(file)
if err != nil {
return nil, errors.Wrapf(err, "reading from file %s failed", file)
}
b, _ := pem.Decode(bytes)
if b == nil { // TODO: also check that the type is what we expect (cert vs key..)
return nil, errors.Errorf("no pem content for file %s", file)
}
return bytes, nil
}
func getPemMaterialFromDir(dir string) ([][]byte, error) {
mspLogger.Debugf("Reading directory %s", dir)
_, err := os.Stat(dir)
if os.IsNotExist(err) {
return nil, err
}
content := make([][]byte, 0)
files, err := os.ReadDir(dir)
if err != nil {
return nil, errors.Wrapf(err, "could not read directory %s", dir)
}
for _, f := range files {
fullName := filepath.Join(dir, f.Name())
f, err := os.Stat(fullName)
if err != nil {
mspLogger.Warningf("Failed to stat %s: %s", fullName, err)
continue
}
if f.IsDir() {
continue
}
mspLogger.Debugf("Inspecting file %s", fullName)
item, err := readPemFile(fullName)
if err != nil {
mspLogger.Warningf("Failed reading file %s: %s", fullName, err)
continue
}
content = append(content, item)
}
return content, nil
}
const (
cacerts = "cacerts"
admincerts = "admincerts"
signcerts = "signcerts"
keystore = "keystore"
intermediatecerts = "intermediatecerts"
crlsfolder = "crls"
configfilename = "config.yaml"
tlscacerts = "tlscacerts"
tlsintermediatecerts = "tlsintermediatecerts"
)
func SetupBCCSPKeystoreConfig(bccspConfig *factory.FactoryOpts, keystoreDir string) *factory.FactoryOpts {
if bccspConfig == nil {
bccspConfig = factory.GetDefaultOpts()
}
if bccspConfig.Default == "SW" || bccspConfig.SW != nil {
if bccspConfig.SW == nil {
bccspConfig.SW = factory.GetDefaultOpts().SW
}
// Only override the KeyStorePath if it was left empty
if bccspConfig.SW.FileKeystore == nil ||
bccspConfig.SW.FileKeystore.KeyStorePath == "" {
bccspConfig.SW.FileKeystore = &factory.FileKeystoreOpts{KeyStorePath: keystoreDir}
}
}
return bccspConfig
}
// GetLocalMspConfigWithType returns a local MSP
// configuration for the MSP in the specified
// directory, with the specified ID and type
func GetLocalMspConfigWithType(dir string, bccspConfig *factory.FactoryOpts, ID, mspType string) (*msp.MSPConfig, error) {
switch mspType {
case ProviderTypeToString(FABRIC):
return GetLocalMspConfig(dir, bccspConfig, ID)
case ProviderTypeToString(IDEMIX):
return idemix.GetIdemixMspConfig(dir, ID)
default:
return nil, errors.Errorf("unknown MSP type '%s'", mspType)
}
}
func GetLocalMspConfig(dir string, bccspConfig *factory.FactoryOpts, ID string) (*msp.MSPConfig, error) {
signcertDir := filepath.Join(dir, signcerts)
keystoreDir := filepath.Join(dir, keystore)
bccspConfig = SetupBCCSPKeystoreConfig(bccspConfig, keystoreDir)
err := factory.InitFactories(bccspConfig)
if err != nil {
return nil, errors.WithMessage(err, "could not initialize BCCSP Factories")
}
signcert, err := getPemMaterialFromDir(signcertDir)
if err != nil || len(signcert) == 0 {
return nil, errors.Wrapf(err, "could not load a valid signer certificate from directory %s", signcertDir)
}
/* FIXME: for now we're making the following assumptions
1) there is exactly one signing cert
2) BCCSP's KeyStore has the private key that matches SKI of
signing cert
*/
sigid := &msp.SigningIdentityInfo{PublicSigner: signcert[0], PrivateSigner: nil}
return getMspConfig(dir, ID, sigid)
}
// GetVerifyingMspConfig returns an MSP config given directory, ID and type
func GetVerifyingMspConfig(dir, ID, mspType string) (*msp.MSPConfig, error) {
switch mspType {
case ProviderTypeToString(FABRIC):
return getMspConfig(dir, ID, nil)
case ProviderTypeToString(IDEMIX):
return idemix.GetIdemixMspConfig(dir, ID)
default:
return nil, errors.Errorf("unknown MSP type '%s'", mspType)
}
}
func getMspConfig(dir string, ID string, sigid *msp.SigningIdentityInfo) (*msp.MSPConfig, error) {
cacertDir := filepath.Join(dir, cacerts)
admincertDir := filepath.Join(dir, admincerts)
intermediatecertsDir := filepath.Join(dir, intermediatecerts)
crlsDir := filepath.Join(dir, crlsfolder)
configFile := filepath.Join(dir, configfilename)
tlscacertDir := filepath.Join(dir, tlscacerts)
tlsintermediatecertsDir := filepath.Join(dir, tlsintermediatecerts)
cacerts, err := getPemMaterialFromDir(cacertDir)
if err != nil || len(cacerts) == 0 {
return nil, errors.WithMessagef(err, "could not load a valid ca certificate from directory %s", cacertDir)
}
admincert, err := getPemMaterialFromDir(admincertDir)
if err != nil && !os.IsNotExist(err) {
return nil, errors.WithMessagef(err, "could not load a valid admin certificate from directory %s", admincertDir)
}
intermediatecerts, err := getPemMaterialFromDir(intermediatecertsDir)
if os.IsNotExist(err) {
mspLogger.Debugf("Intermediate certs folder not found at [%s]. Skipping. [%s]", intermediatecertsDir, err)
} else if err != nil {
return nil, errors.WithMessagef(err, "failed loading intermediate ca certs at [%s]", intermediatecertsDir)
}
tlsCACerts, err := getPemMaterialFromDir(tlscacertDir)
tlsIntermediateCerts := [][]byte{}
if os.IsNotExist(err) {
mspLogger.Debugf("TLS CA certs folder not found at [%s]. Skipping and ignoring TLS intermediate CA folder. [%s]", tlsintermediatecertsDir, err)
} else if err != nil {
return nil, errors.WithMessagef(err, "failed loading TLS ca certs at [%s]", tlsintermediatecertsDir)
} else if len(tlsCACerts) != 0 {
tlsIntermediateCerts, err = getPemMaterialFromDir(tlsintermediatecertsDir)
if os.IsNotExist(err) {
mspLogger.Debugf("TLS intermediate certs folder not found at [%s]. Skipping. [%s]", tlsintermediatecertsDir, err)
} else if err != nil {
return nil, errors.WithMessagef(err, "failed loading TLS intermediate ca certs at [%s]", tlsintermediatecertsDir)
}
} else {
mspLogger.Debugf("TLS CA certs folder at [%s] is empty. Skipping.", tlsintermediatecertsDir)
}
crls, err := getPemMaterialFromDir(crlsDir)
if os.IsNotExist(err) {
mspLogger.Debugf("crls folder not found at [%s]. Skipping. [%s]", crlsDir, err)
} else if err != nil {
return nil, errors.WithMessagef(err, "failed loading crls at [%s]", crlsDir)
}
// Load configuration file
// if the configuration file is there then load it
// otherwise skip it
var ouis []*msp.FabricOUIdentifier
var nodeOUs *msp.FabricNodeOUs
_, err = os.Stat(configFile)
if err == nil {
// load the file, if there is a failure in loading it then
// return an error
raw, err := os.ReadFile(configFile)
if err != nil {
return nil, errors.Wrapf(err, "failed loading configuration file at [%s]", configFile)
}
configuration := Configuration{}
err = yaml.Unmarshal(raw, &configuration)
if err != nil {
return nil, errors.Wrapf(err, "failed unmarshalling configuration file at [%s]", configFile)
}
// Prepare OrganizationalUnitIdentifiers
if len(configuration.OrganizationalUnitIdentifiers) > 0 {
for _, ouID := range configuration.OrganizationalUnitIdentifiers {
f := filepath.Join(dir, ouID.Certificate)
raw, err = readFile(f)
if err != nil {
return nil, errors.Wrapf(err, "failed loading OrganizationalUnit certificate at [%s]", f)
}
oui := &msp.FabricOUIdentifier{
Certificate: raw,
OrganizationalUnitIdentifier: ouID.OrganizationalUnitIdentifier,
}
ouis = append(ouis, oui)
}
}
// Prepare NodeOUs
if configuration.NodeOUs != nil && configuration.NodeOUs.Enable {
mspLogger.Debug("Loading NodeOUs")
nodeOUs = &msp.FabricNodeOUs{
Enable: true,
}
if configuration.NodeOUs.ClientOUIdentifier != nil && len(configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier) != 0 {
nodeOUs.ClientOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.ClientOUIdentifier.OrganizationalUnitIdentifier}
}
if configuration.NodeOUs.PeerOUIdentifier != nil && len(configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier) != 0 {
nodeOUs.PeerOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.PeerOUIdentifier.OrganizationalUnitIdentifier}
}
if configuration.NodeOUs.AdminOUIdentifier != nil && len(configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier) != 0 {
nodeOUs.AdminOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.AdminOUIdentifier.OrganizationalUnitIdentifier}
}
if configuration.NodeOUs.OrdererOUIdentifier != nil && len(configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier) != 0 {
nodeOUs.OrdererOuIdentifier = &msp.FabricOUIdentifier{OrganizationalUnitIdentifier: configuration.NodeOUs.OrdererOUIdentifier.OrganizationalUnitIdentifier}
}
// Read certificates, if defined
// ClientOU
if nodeOUs.ClientOuIdentifier != nil {
nodeOUs.ClientOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.ClientOUIdentifier.Certificate, "ClientOU")
}
// PeerOU
if nodeOUs.PeerOuIdentifier != nil {
nodeOUs.PeerOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.PeerOUIdentifier.Certificate, "PeerOU")
}
// AdminOU
if nodeOUs.AdminOuIdentifier != nil {
nodeOUs.AdminOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.AdminOUIdentifier.Certificate, "AdminOU")
}
// OrdererOU
if nodeOUs.OrdererOuIdentifier != nil {
nodeOUs.OrdererOuIdentifier.Certificate = loadCertificateAt(dir, configuration.NodeOUs.OrdererOUIdentifier.Certificate, "OrdererOU")
}
}
} else {
mspLogger.Debugf("MSP configuration file not found at [%s]: [%s]", configFile, err)
}
// Set FabricCryptoConfig
cryptoConfig := &msp.FabricCryptoConfig{
SignatureHashFamily: bccsp.SHA2,
IdentityIdentifierHashFunction: bccsp.SHA256,
}
// Compose FabricMSPConfig
fmspconf := &msp.FabricMSPConfig{
Admins: admincert,
RootCerts: cacerts,
IntermediateCerts: intermediatecerts,
SigningIdentity: sigid,
Name: ID,
OrganizationalUnitIdentifiers: ouis,
RevocationList: crls,
CryptoConfig: cryptoConfig,
TlsRootCerts: tlsCACerts,
TlsIntermediateCerts: tlsIntermediateCerts,
FabricNodeOus: nodeOUs,
}
fmpsjs, err := proto.Marshal(fmspconf)
if err != nil {
return nil, err
}
return &msp.MSPConfig{Config: fmpsjs, Type: int32(FABRIC)}, nil
}
func loadCertificateAt(dir, certificatePath string, ouType string) []byte {
if certificatePath == "" {
mspLogger.Debugf("Specific certificate for %s is not configured", ouType)
return nil
}
f := filepath.Join(dir, certificatePath)
raw, err := readFile(f)
if err != nil {
mspLogger.Warnf("Failed loading %s certificate at [%s]: [%s]", ouType, f, err)
} else {
return raw
}
return nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"github.com/IBM/idemix"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/pkg/errors"
)
type MSPVersion int
const (
MSPv1_0 = iota
MSPv1_1
MSPv1_3
MSPv1_4_3
MSPv3_0
)
// NewOpts represent
type NewOpts interface {
// GetVersion returns the MSP's version to be instantiated
GetVersion() MSPVersion
}
// NewBaseOpts is the default base type for all MSP instantiation Opts
type NewBaseOpts struct {
Version MSPVersion
}
func (o *NewBaseOpts) GetVersion() MSPVersion {
return o.Version
}
// BCCSPNewOpts contains the options to instantiate a new BCCSP-based (X509) MSP
type BCCSPNewOpts struct {
NewBaseOpts
}
// IdemixNewOpts contains the options to instantiate a new Idemix-based MSP
type IdemixNewOpts struct {
NewBaseOpts
}
// New create a new MSP instance depending on the passed Opts
func New(opts NewOpts, cryptoProvider bccsp.BCCSP) (MSP, error) {
switch opts.(type) {
case *BCCSPNewOpts:
switch opts.GetVersion() {
case MSPv1_0, MSPv1_1, MSPv1_3, MSPv1_4_3, MSPv3_0:
return newBccspMsp(opts.GetVersion(), cryptoProvider)
default:
return nil, errors.Errorf("Invalid *BCCSPNewOpts. Version not recognized [%v]", opts.GetVersion())
}
case *IdemixNewOpts:
switch opts.GetVersion() {
case MSPv1_3, MSPv1_4_3:
msp, err := idemix.NewIdemixMsp(MSPv1_3)
if err != nil {
return nil, err
}
return &idemixMSPWrapper{msp.(*idemix.Idemixmsp)}, nil
case MSPv1_1:
msp, err := idemix.NewIdemixMsp(MSPv1_1)
if err != nil {
return nil, err
}
return &idemixMSPWrapper{msp.(*idemix.Idemixmsp)}, nil
default:
return nil, errors.Errorf("Invalid *IdemixNewOpts. Version not recognized [%v]", opts.GetVersion())
}
default:
return nil, errors.Errorf("Invalid msp.NewOpts instance. It must be either *BCCSPNewOpts or *IdemixNewOpts. It was [%v]", opts)
}
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"github.com/IBM/idemix"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
)
type idemixSigningIdentityWrapper struct {
*idemix.IdemixSigningIdentity
}
func (i *idemixSigningIdentityWrapper) GetPublicVersion() Identity {
return &idemixIdentityWrapper{Idemixidentity: i.IdemixSigningIdentity.GetPublicVersion().(*idemix.Idemixidentity)}
}
func (i *idemixSigningIdentityWrapper) GetIdentifier() *IdentityIdentifier {
return i.GetPublicVersion().GetIdentifier()
}
func (i *idemixSigningIdentityWrapper) GetOrganizationalUnits() []*OUIdentifier {
return i.GetPublicVersion().GetOrganizationalUnits()
}
type idemixIdentityWrapper struct {
*idemix.Idemixidentity
}
func (i *idemixIdentityWrapper) GetIdentifier() *IdentityIdentifier {
id := i.Idemixidentity.GetIdentifier()
return &IdentityIdentifier{
Mspid: id.Mspid,
Id: id.Id,
}
}
func (i *idemixIdentityWrapper) GetOrganizationalUnits() []*OUIdentifier {
ous := i.Idemixidentity.GetOrganizationalUnits()
wous := []*OUIdentifier{}
for _, ou := range ous {
wous = append(wous, &OUIdentifier{
CertifiersIdentifier: ou.CertifiersIdentifier,
OrganizationalUnitIdentifier: ou.OrganizationalUnitIdentifier,
})
}
return wous
}
type idemixMSPWrapper struct {
*idemix.Idemixmsp
}
func (i *idemixMSPWrapper) deserializeIdentityInternal(serializedIdentity []byte) (Identity, error) {
id, err := i.Idemixmsp.DeserializeIdentityInternal(serializedIdentity)
if err != nil {
return nil, err
}
return &idemixIdentityWrapper{id.(*idemix.Idemixidentity)}, nil
}
func (i *idemixMSPWrapper) DeserializeIdentity(serializedIdentity []byte) (Identity, error) {
id, err := i.Idemixmsp.DeserializeIdentity(serializedIdentity)
if err != nil {
return nil, err
}
return &idemixIdentityWrapper{id.(*idemix.Idemixidentity)}, nil
}
func (i *idemixMSPWrapper) GetVersion() MSPVersion {
return MSPVersion(i.Idemixmsp.GetVersion())
}
func (i *idemixMSPWrapper) GetType() ProviderType {
return ProviderType(i.Idemixmsp.GetType())
}
func (i *idemixMSPWrapper) GetDefaultSigningIdentity() (SigningIdentity, error) {
id, err := i.Idemixmsp.GetDefaultSigningIdentity()
if err != nil {
return nil, err
}
return &idemixSigningIdentityWrapper{id.(*idemix.IdemixSigningIdentity)}, nil
}
func (i *idemixMSPWrapper) Validate(id Identity) error {
return i.Idemixmsp.Validate(id.(*idemixIdentityWrapper).Idemixidentity)
}
func (i *idemixMSPWrapper) SatisfiesPrincipal(id Identity, principal *msp.MSPPrincipal) error {
return i.Idemixmsp.SatisfiesPrincipal(id.(*idemixIdentityWrapper).Idemixidentity, principal)
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"crypto"
"crypto/rand"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"sync"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
"google.golang.org/protobuf/proto"
)
var mspIdentityLogger = flogging.MustGetLogger("msp.identity")
type identity struct {
// id contains the identifier (MSPID and identity identifier) for this instance
id *IdentityIdentifier
// cert contains the x.509 certificate that signs the public key of this instance
cert *x509.Certificate
// this is the public key of this instance
pk bccsp.Key
// reference to the MSP that "owns" this identity
msp *bccspmsp
// validationMutex is used to synchronise memory operation
// over validated and validationErr
validationMutex sync.Mutex
// validated is true when the validateIdentity function
// has been called on this instance
validated bool
// validationErr contains the validation error for this
// instance. It can be read if validated is true
validationErr error
}
func newIdentity(cert *x509.Certificate, pk bccsp.Key, msp *bccspmsp) (Identity, error) {
if mspIdentityLogger.IsEnabledFor(zapcore.DebugLevel) {
mspIdentityLogger.Debugf("Creating identity instance for cert %s", certToPEM(cert))
}
// Sanitize first the certificate
cert, err := msp.sanitizeCert(cert)
if err != nil {
return nil, err
}
// Compute identity identifier
// Use the hash of the identity's certificate as id in the IdentityIdentifier
hashOpt, err := bccsp.GetHashOpt(msp.cryptoConfig.IdentityIdentifierHashFunction)
if err != nil {
return nil, errors.WithMessage(err, "failed getting hash function options")
}
digest, err := msp.bccsp.Hash(cert.Raw, hashOpt)
if err != nil {
return nil, errors.WithMessage(err, "failed hashing raw certificate to compute the id of the IdentityIdentifier")
}
id := &IdentityIdentifier{
Mspid: msp.name,
Id: hex.EncodeToString(digest),
}
return &identity{id: id, cert: cert, pk: pk, msp: msp}, nil
}
// ExpiresAt returns the time at which the Identity expires.
func (id *identity) ExpiresAt() time.Time {
return id.cert.NotAfter
}
// SatisfiesPrincipal returns nil if this instance matches the supplied principal or an error otherwise
func (id *identity) SatisfiesPrincipal(principal *msp.MSPPrincipal) error {
return id.msp.SatisfiesPrincipal(id, principal)
}
// GetIdentifier returns the identifier (MSPID/IDID) for this instance
func (id *identity) GetIdentifier() *IdentityIdentifier {
return id.id
}
// GetMSPIdentifier returns the MSP identifier for this instance
func (id *identity) GetMSPIdentifier() string {
return id.id.Mspid
}
// Validate returns nil if this instance is a valid identity or an error otherwise
func (id *identity) Validate() error {
return id.msp.Validate(id)
}
type OUIDs []*OUIdentifier
func (o OUIDs) String() string {
var res []string
for _, id := range o {
res = append(res, fmt.Sprintf("%s(%X)", id.OrganizationalUnitIdentifier, id.CertifiersIdentifier[0:8]))
}
return fmt.Sprintf("%s", res)
}
// GetOrganizationalUnits returns the OU for this instance
func (id *identity) GetOrganizationalUnits() []*OUIdentifier {
if id.cert == nil {
return nil
}
cid, err := id.msp.getCertificationChainIdentifier(id)
if err != nil {
mspIdentityLogger.Errorf("Failed getting certification chain identifier for [%v]: [%+v]", id, err)
return nil
}
var res []*OUIdentifier
for _, unit := range id.cert.Subject.OrganizationalUnit {
res = append(res, &OUIdentifier{
OrganizationalUnitIdentifier: unit,
CertifiersIdentifier: cid,
})
}
return res
}
// Anonymous returns true if this identity provides anonymity
func (id *identity) Anonymous() bool {
return false
}
// NewSerializedIdentity returns a serialized identity
// having as content the passed mspID and x509 certificate in PEM format.
// This method does not check the validity of certificate nor
// any consistency of the mspID with it.
func NewSerializedIdentity(mspID string, certPEM []byte) ([]byte, error) {
// We serialize identities by prepending the MSPID
// and appending the x509 cert in PEM format
sId := &msp.SerializedIdentity{Mspid: mspID, IdBytes: certPEM}
raw, err := proto.Marshal(sId)
if err != nil {
return nil, errors.Wrapf(err, "failed serializing identity [%s][%X]", mspID, certPEM)
}
return raw, nil
}
// Verify checks against a signature and a message
// to determine whether this identity produced the
// signature; it returns nil if so or an error otherwise
func (id *identity) Verify(msg []byte, sig []byte) error {
// mspIdentityLogger.Infof("Verifying signature")
digestOrMsg := msg
// Compute Hash if not Ed25519
// Ideally this method should be algorithm agnostic
// but golang requires the hash for ecdsa and requires
// the full message for ed25519
if id.cert.PublicKeyAlgorithm != x509.Ed25519 {
hashOpt, err := id.getHashOpt(id.msp.cryptoConfig.SignatureHashFamily)
if err != nil {
return errors.WithMessage(err, "failed getting hash function options")
}
digestOrMsg, err = id.msp.bccsp.Hash(msg, hashOpt)
if err != nil {
return errors.WithMessage(err, "failed computing digest")
}
}
if mspIdentityLogger.IsEnabledFor(zapcore.DebugLevel) {
mspIdentityLogger.Debugf("Verify: signer identity (certificate subject=%s issuer=%s serialnumber=%d)", id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
// mspIdentityLogger.Debugf("Verify: digest = %s", hex.Dump(digest))
// mspIdentityLogger.Debugf("Verify: sig = %s", hex.Dump(sig))
}
valid, err := id.msp.bccsp.Verify(id.pk, sig, digestOrMsg, nil)
if err != nil {
return errors.WithMessage(err, "could not determine the validity of the signature")
} else if !valid {
mspIdentityLogger.Warnf("The signature is invalid for (certificate subject=%s issuer=%s serialnumber=%d)", id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
return errors.New("The signature is invalid")
}
return nil
}
// Serialize returns a byte array representation of this identity
func (id *identity) Serialize() ([]byte, error) {
pb := &pem.Block{Bytes: id.cert.Raw, Type: "CERTIFICATE"}
pemBytes := pem.EncodeToMemory(pb)
if pemBytes == nil {
return nil, errors.New("encoding of identity failed")
}
// We serialize identities by prepending the MSPID and appending the ASN.1 DER content of the cert
sId := &msp.SerializedIdentity{Mspid: id.id.Mspid, IdBytes: pemBytes}
idBytes, err := proto.Marshal(sId)
if err != nil {
return nil, errors.Wrapf(err, "could not marshal a SerializedIdentity structure for identity %s", id.id)
}
return idBytes, nil
}
func (id *identity) getHashOpt(hashFamily string) (bccsp.HashOpts, error) {
switch hashFamily {
case bccsp.SHA2:
return bccsp.GetHashOpt(bccsp.SHA256)
case bccsp.SHA3:
return bccsp.GetHashOpt(bccsp.SHA3_256)
}
return nil, errors.Errorf("hash family not recognized [%s]", hashFamily)
}
type signingidentity struct {
// we embed everything from a base identity
identity
// signer corresponds to the object that can produce signatures from this identity
signer crypto.Signer
}
func newSigningIdentity(cert *x509.Certificate, pk bccsp.Key, signer crypto.Signer, msp *bccspmsp) (SigningIdentity, error) {
// mspIdentityLogger.Infof("Creating signing identity instance for ID %s", id)
mspId, err := newIdentity(cert, pk, msp)
if err != nil {
return nil, err
}
return &signingidentity{
identity: identity{
id: mspId.(*identity).id,
cert: mspId.(*identity).cert,
msp: mspId.(*identity).msp,
pk: mspId.(*identity).pk,
},
signer: signer,
}, nil
}
// Sign produces a signature over msg, signed by this instance
func (id *signingidentity) Sign(msg []byte) ([]byte, error) {
// mspIdentityLogger.Infof("Signing message")
digestOrMsg := msg
// Compute Hash if not Ed25519
// Ideally this method should be algorithm agnostic
// but golang requires the hash for ecdsa and requires
// the full message for ed25519
if id.identity.cert.PublicKeyAlgorithm != x509.Ed25519 {
hashOpt, err := id.getHashOpt(id.msp.cryptoConfig.SignatureHashFamily)
if err != nil {
return nil, errors.WithMessage(err, "failed getting hash function options")
}
digestOrMsg, err = id.msp.bccsp.Hash(msg, hashOpt)
if err != nil {
return nil, errors.WithMessage(err, "failed computing digest")
}
}
if len(msg) < 32 {
mspIdentityLogger.Debugf("Sign: plaintext: %X \n", msg)
} else {
mspIdentityLogger.Debugf("Sign: plaintext: %X...%X \n", msg[0:16], msg[len(msg)-16:])
}
if id.identity.cert.PublicKeyAlgorithm != x509.Ed25519 {
mspIdentityLogger.Debugf("Sign: digest: %X \n", digestOrMsg)
}
// Sign digest for ECDSA or msg for ED25519
return id.signer.Sign(rand.Reader, digestOrMsg, nil)
}
// GetPublicVersion returns the public version of this identity,
// namely, the one that is only able to verify messages and not sign them
func (id *signingidentity) GetPublicVersion() Identity {
return &id.identity
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"time"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
)
// IdentityDeserializer is implemented by both MSPManger and MSP
type IdentityDeserializer interface {
// DeserializeIdentity deserializes an identity.
// Deserialization will fail if the identity is associated to
// an msp that is different from this one that is performing
// the deserialization.
DeserializeIdentity(serializedIdentity []byte) (Identity, error)
// IsWellFormed checks if the given identity can be deserialized into its provider-specific form
IsWellFormed(identity *msp.SerializedIdentity) error
}
// Membership service provider APIs for Hyperledger Fabric:
//
// By "membership service provider" we refer to an abstract component of the
// system that would provide (anonymous) credentials to clients, and peers for
// them to participate in Hyperledger/fabric network. Clients use these
// credentials to authenticate their transactions, and peers use these credentials
// to authenticate transaction processing results (endorsements). While
// strongly connected to the transaction processing components of the systems,
// this interface aims to have membership services components defined, in such
// a way such that alternate implementations of this can be smoothly plugged in
// without modifying the core of transaction processing components of the system.
//
// This file includes Membership service provider interface that covers the
// needs of a peer membership service provider interface.
// MSPManager is an interface defining a manager of one or more MSPs. This
// essentially acts as a mediator to MSP calls and routes MSP related calls
// to the appropriate MSP.
// This object is immutable, it is initialized once and never changed.
type MSPManager interface {
// IdentityDeserializer interface needs to be implemented by MSPManager
IdentityDeserializer
// Setup the MSP manager instance according to configuration information
Setup(msps []MSP) error
// GetMSPs Provides a list of Membership Service providers
GetMSPs() (map[string]MSP, error)
}
// MSP is the minimal Membership Service Provider Interface to be implemented
// to accommodate peer functionality
type MSP interface {
// IdentityDeserializer interface needs to be implemented by MSP
IdentityDeserializer
// Setup the MSP instance according to configuration information
Setup(config *msp.MSPConfig) error
// GetVersion returns the version of this MSP
GetVersion() MSPVersion
// GetType returns the provider type
GetType() ProviderType
// GetIdentifier returns the provider identifier
GetIdentifier() (string, error)
// GetDefaultSigningIdentity returns the default signing identity
GetDefaultSigningIdentity() (SigningIdentity, error)
// GetTLSRootCerts returns the TLS root certificates for this MSP
GetTLSRootCerts() [][]byte
// GetTLSIntermediateCerts returns the TLS intermediate root certificates for this MSP
GetTLSIntermediateCerts() [][]byte
// Validate checks whether the supplied identity is valid
Validate(id Identity) error
// SatisfiesPrincipal checks whether the identity matches
// the description supplied in MSPPrincipal. The check may
// involve a byte-by-byte comparison (if the principal is
// a serialized identity) or may require MSP validation
SatisfiesPrincipal(id Identity, principal *msp.MSPPrincipal) error
}
// OUIdentifier represents an organizational unit and
// its related chain of trust identifier.
type OUIdentifier struct {
// CertifiersIdentifier is the hash of certificates chain of trust
// related to this organizational unit
CertifiersIdentifier []byte
// OrganizationalUnitIdentifier defines the organizational unit under the
// MSP identified with MSPIdentifier
OrganizationalUnitIdentifier string
}
// From this point on, there are interfaces that are shared within the peer and client API
// of the membership service provider.
// Identity interface defining operations associated to a "certificate".
// That is, the public part of the identity could be thought to be a certificate,
// and offers solely signature verification capabilities. This is to be used
// at the peer side when verifying certificates that transactions are signed
// with, and verifying signatures that correspond to these certificates.///
type Identity interface {
// ExpiresAt returns the time at which the Identity expires.
// If the returned time is the zero value, it implies
// the Identity does not expire, or that its expiration
// time is unknown
ExpiresAt() time.Time
// GetIdentifier returns the identifier of that identity
GetIdentifier() *IdentityIdentifier
// GetMSPIdentifier returns the MSP Id for this instance
GetMSPIdentifier() string
// Validate uses the rules that govern this identity to validate it.
// E.g., if it is a fabric TCert implemented as identity, validate
// will check the TCert signature against the assumed root certificate
// authority.
Validate() error
// GetOrganizationalUnits returns zero or more organization units or
// divisions this identity is related to as long as this is public
// information. Certain MSP implementations may use attributes
// that are publicly associated to this identity, or the identifier of
// the root certificate authority that has provided signatures on this
// certificate.
// Examples:
// - if the identity is an x.509 certificate, this function returns one
// or more string which is encoded in the Subject's Distinguished Name
// of the type OU
// TODO: For X.509 based identities, check if we need a dedicated type
// for OU where the Certificate OU is properly namespaced by the
// signer's identity
GetOrganizationalUnits() []*OUIdentifier
// Anonymous returns true if this is an anonymous identity, false otherwise
Anonymous() bool
// Verify a signature over some message using this identity as reference
Verify(msg []byte, sig []byte) error
// Serialize converts an identity to bytes
Serialize() ([]byte, error)
// SatisfiesPrincipal checks whether this instance matches
// the description supplied in MSPPrincipal. The check may
// involve a byte-by-byte comparison (if the principal is
// a serialized identity) or may require MSP validation
SatisfiesPrincipal(principal *msp.MSPPrincipal) error
}
// SigningIdentity is an extension of Identity to cover signing capabilities.
// E.g., signing identity should be requested in the case of a client who wishes
// to sign transactions, or fabric endorser who wishes to sign proposal
// processing outcomes.
type SigningIdentity interface {
// Extends Identity
Identity
// Sign the message
Sign(msg []byte) ([]byte, error)
// GetPublicVersion returns the public parts of this identity
GetPublicVersion() Identity
}
// IdentityIdentifier is a holder for the identifier of a specific
// identity, naturally namespaced, by its provider identifier.
type IdentityIdentifier struct {
// The identifier of the associated membership service provider
Mspid string
// The identifier for an identity within a provider
Id string
}
// ProviderType indicates the type of an identity provider
type ProviderType int
// The ProviderType of a member relative to the member API
const (
FABRIC ProviderType = iota // MSP is of FABRIC type
IDEMIX // MSP is of IDEMIX type
OTHER // MSP is of OTHER TYPE
// NOTE: as new types are added to this set,
// the mspTypes map below must be extended
)
var mspTypeStrings = map[ProviderType]string{
FABRIC: "bccsp",
IDEMIX: "idemix",
}
var Options = map[string]NewOpts{
ProviderTypeToString(FABRIC): &BCCSPNewOpts{NewBaseOpts: NewBaseOpts{Version: MSPv3_0}},
ProviderTypeToString(IDEMIX): &IdemixNewOpts{NewBaseOpts: NewBaseOpts{Version: MSPv1_1}},
}
// ProviderTypeToString returns a string that represents the ProviderType integer
func ProviderTypeToString(id ProviderType) string {
if res, found := mspTypeStrings[id]; found {
return res
}
return ""
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package msp
func FuzzDeserializeIdentity(data []byte) int {
m := &mspManagerImpl{}
m.up = true
_, _ = m.DeserializeIdentity(data)
return 1
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"bytes"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"encoding/pem"
"fmt"
"strings"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/factory"
"github.com/hyperledger/fabric-lib-go/bccsp/signer"
"github.com/hyperledger/fabric-lib-go/bccsp/sw"
"github.com/hyperledger/fabric-lib-go/bccsp/utils"
m "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// mspSetupFuncType is the prototype of the setup function
type mspSetupFuncType func(config *m.FabricMSPConfig) error
// validateIdentityOUsFuncType is the prototype of the function to validate identity's OUs
type validateIdentityOUsFuncType func(id *identity) error
// satisfiesPrincipalInternalFuncType is the prototype of the function to check if principals are satisfied
type satisfiesPrincipalInternalFuncType func(id Identity, principal *m.MSPPrincipal) error
// setupAdminInternalFuncType is a prototype of the function to setup the admins
type setupAdminInternalFuncType func(conf *m.FabricMSPConfig) error
// This is an instantiation of an MSP that
// uses BCCSP for its cryptographic primitives.
type bccspmsp struct {
// version specifies the behaviour of this msp
version MSPVersion
// The following function pointers are used to change the behaviour
// of this MSP depending on its version.
// internalSetupFunc is the pointer to the setup function
internalSetupFunc mspSetupFuncType
// internalValidateIdentityOusFunc is the pointer to the function to validate identity's OUs
internalValidateIdentityOusFunc validateIdentityOUsFuncType
// internalSatisfiesPrincipalInternalFunc is the pointer to the function to check if principals are satisfied
internalSatisfiesPrincipalInternalFunc satisfiesPrincipalInternalFuncType
// internalSetupAdmin is the pointer to the function that setup the administrators of this msp
internalSetupAdmin setupAdminInternalFuncType
// list of CA certs we trust
rootCerts []Identity
// list of intermediate certs we trust
intermediateCerts []Identity
// list of CA TLS certs we trust
tlsRootCerts [][]byte
// list of intermediate TLS certs we trust
tlsIntermediateCerts [][]byte
// certificationTreeInternalNodesMap whose keys correspond to the raw material
// (DER representation) of a certificate casted to a string, and whose values
// are boolean. True means that the certificate is an internal node of the certification tree.
// False means that the certificate corresponds to a leaf of the certification tree.
certificationTreeInternalNodesMap map[string]bool
// list of signing identities
signer SigningIdentity
// list of admin identities
admins []Identity
// the crypto provider
bccsp bccsp.BCCSP
// the provider identifier for this MSP
name string
// verification options for MSP members
opts *x509.VerifyOptions
// list of certificate revocation lists
CRL []*pkix.CertificateList
// list of OUs
ouIdentifiers map[string][][]byte
// cryptoConfig contains
cryptoConfig *m.FabricCryptoConfig
// supportedPublicKeyAlgorithms supported by this msp
supportedPublicKeyAlgorithms map[x509.PublicKeyAlgorithm]bool
// NodeOUs configuration
ouEnforcement bool
// These are the OUIdentifiers of the clients, peers, admins and orderers.
// They are used to tell apart these entities
clientOU, peerOU, adminOU, ordererOU *OUIdentifier
}
// newBccspMsp returns an MSP instance backed up by a BCCSP
// crypto provider. It handles x.509 certificates and can
// generate identities and signing identities backed by
// certificates and keypairs
func newBccspMsp(version MSPVersion, defaultBCCSP bccsp.BCCSP) (MSP, error) {
mspLogger.Debugf("Creating BCCSP-based MSP instance")
theMsp := &bccspmsp{}
theMsp.version = version
theMsp.bccsp = defaultBCCSP
switch version {
case MSPv1_0:
theMsp.internalSetupFunc = theMsp.setupV1
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV1
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
case MSPv1_1:
theMsp.internalSetupFunc = theMsp.setupV11
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalPreV13
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
case MSPv1_3:
theMsp.internalSetupFunc = theMsp.setupV11
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV11
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV13
theMsp.internalSetupAdmin = theMsp.setupAdminsPreV142
case MSPv1_4_3:
theMsp.internalSetupFunc = theMsp.setupV142
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV142
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV142
theMsp.internalSetupAdmin = theMsp.setupAdminsV142
case MSPv3_0:
theMsp.internalSetupFunc = theMsp.setupV3
theMsp.internalValidateIdentityOusFunc = theMsp.validateIdentityOUsV142
theMsp.internalSatisfiesPrincipalInternalFunc = theMsp.satisfiesPrincipalInternalV142
theMsp.internalSetupAdmin = theMsp.setupAdminsV142
default:
return nil, errors.Errorf("Invalid MSP version [%v]", version)
}
return theMsp, nil
}
// NewBccspMspWithKeyStore allows to create a BCCSP-based MSP whose underlying
// crypto material is available through the passed keystore
func NewBccspMspWithKeyStore(version MSPVersion, keyStore bccsp.KeyStore, bccsp bccsp.BCCSP) (MSP, error) {
thisMSP, err := newBccspMsp(version, bccsp)
if err != nil {
return nil, err
}
csp, err := sw.NewWithParams(
factory.GetDefaultOpts().SW.Security,
factory.GetDefaultOpts().SW.Hash,
keyStore)
if err != nil {
return nil, err
}
thisMSP.(*bccspmsp).bccsp = csp
return thisMSP, nil
}
func (msp *bccspmsp) getCertFromPem(idBytes []byte) (*x509.Certificate, error) {
if idBytes == nil {
return nil, errors.New("getCertFromPem error: nil idBytes")
}
// Decode the pem bytes
pemCert, _ := pem.Decode(idBytes)
if pemCert == nil {
return nil, errors.Errorf("getCertFromPem error: could not decode pem bytes [%v]", idBytes)
}
// get a cert
var cert *x509.Certificate
cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
return nil, errors.Wrap(err, "getCertFromPem error: failed to parse x509 cert")
}
return cert, nil
}
func (msp *bccspmsp) getIdentityFromConf(idBytes []byte) (Identity, bccsp.Key, error) {
// get a cert
cert, err := msp.getCertFromPem(idBytes)
if err != nil {
return nil, nil, err
}
// get the public key in the right format
certPubK, err := msp.bccsp.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
if err != nil {
return nil, nil, err
}
mspId, err := newIdentity(cert, certPubK, msp)
if err != nil {
return nil, nil, err
}
return mspId, certPubK, nil
}
func (msp *bccspmsp) getSigningIdentityFromConf(sidInfo *m.SigningIdentityInfo) (SigningIdentity, error) {
if sidInfo == nil {
return nil, errors.New("getIdentityFromBytes error: nil sidInfo")
}
// Extract the public part of the identity
idPub, pubKey, err := msp.getIdentityFromConf(sidInfo.PublicSigner)
if err != nil {
return nil, err
}
// Find the matching private key in the BCCSP keystore
privKey, err := msp.bccsp.GetKey(pubKey.SKI())
// Less Secure: Attempt to import Private Key from KeyInfo, if BCCSP was not able to find the key
if err != nil {
mspLogger.Debugf("Could not find SKI [%s], trying KeyMaterial field: %+v\n", hex.EncodeToString(pubKey.SKI()), err)
if sidInfo.PrivateSigner == nil || sidInfo.PrivateSigner.KeyMaterial == nil {
return nil, errors.New("KeyMaterial not found in SigningIdentityInfo")
}
pemKey, _ := pem.Decode(sidInfo.PrivateSigner.KeyMaterial)
if pemKey == nil {
return nil, errors.Errorf("%s: wrong PEM encoding", sidInfo.PrivateSigner.KeyIdentifier)
}
privKey, err = msp.bccsp.KeyImport(pemKey.Bytes, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: true})
if err != nil {
return nil, errors.WithMessage(err, "getIdentityFromBytes error: Failed to import EC private key")
}
}
// get the peer signer
peerSigner, err := signer.New(msp.bccsp, privKey)
if err != nil {
return nil, errors.WithMessage(err, "getIdentityFromBytes error: Failed initializing bccspCryptoSigner")
}
return newSigningIdentity(idPub.(*identity).cert, idPub.(*identity).pk, peerSigner, msp)
}
// Setup sets up the internal data structures
// for this MSP, given an MSPConfig ref; it
// returns nil in case of success or an error otherwise
func (msp *bccspmsp) Setup(conf1 *m.MSPConfig) error {
if conf1 == nil {
return errors.New("Setup error: nil conf reference")
}
// given that it's an msp of type fabric, extract the MSPConfig instance
conf := &m.FabricMSPConfig{}
err := proto.Unmarshal(conf1.Config, conf)
if err != nil {
return errors.Wrap(err, "failed unmarshalling fabric msp config")
}
// set the name for this msp
msp.name = conf.Name
mspLogger.Debugf("Setting up MSP instance %s", msp.name)
// setup
return msp.internalSetupFunc(conf)
}
// GetVersion returns the version of this MSP
func (msp *bccspmsp) GetVersion() MSPVersion {
return msp.version
}
// GetType returns the type for this MSP
func (msp *bccspmsp) GetType() ProviderType {
return FABRIC
}
// GetIdentifier returns the MSP identifier for this instance
func (msp *bccspmsp) GetIdentifier() (string, error) {
return msp.name, nil
}
// GetTLSRootCerts returns the root certificates for this MSP
func (msp *bccspmsp) GetTLSRootCerts() [][]byte {
return msp.tlsRootCerts
}
// GetTLSIntermediateCerts returns the intermediate root certificates for this MSP
func (msp *bccspmsp) GetTLSIntermediateCerts() [][]byte {
return msp.tlsIntermediateCerts
}
// GetDefaultSigningIdentity returns the
// default signing identity for this MSP (if any)
func (msp *bccspmsp) GetDefaultSigningIdentity() (SigningIdentity, error) {
mspLogger.Debugf("Obtaining default signing identity")
if msp.signer == nil {
return nil, errors.New("this MSP does not possess a valid default signing identity")
}
return msp.signer, nil
}
// Validate attempts to determine whether
// the supplied identity is valid according
// to this MSP's roots of trust; it returns
// nil in case the identity is valid or an
// error otherwise
func (msp *bccspmsp) Validate(id Identity) error {
mspLogger.Debugf("MSP %s validating identity", msp.name)
switch id := id.(type) {
// If this identity is of this specific type,
// this is how I can validate it given the
// root of trust this MSP has
case *identity:
return msp.validateIdentity(id)
default:
return errors.New("identity type not recognized")
}
}
// hasOURole checks that the identity belongs to the organizational unit
// associated to the specified MSPRole.
// This function does not check the certifiers identifier.
// Appropriate validation needs to be enforced before.
func (msp *bccspmsp) hasOURole(id Identity, mspRole m.MSPRole_MSPRoleType) error {
// Check NodeOUs
if !msp.ouEnforcement {
return errors.New("NodeOUs not activated. Cannot tell apart identities.")
}
mspLogger.Debugf("MSP %s checking if the identity is a client", msp.name)
switch id := id.(type) {
// If this identity is of this specific type,
// this is how I can validate it given the
// root of trust this MSP has
case *identity:
return msp.hasOURoleInternal(id, mspRole)
default:
return errors.New("Identity type not recognized")
}
}
func (msp *bccspmsp) hasOURoleInternal(id *identity, mspRole m.MSPRole_MSPRoleType) error {
var nodeOU *OUIdentifier
switch mspRole {
case m.MSPRole_CLIENT:
nodeOU = msp.clientOU
case m.MSPRole_PEER:
nodeOU = msp.peerOU
case m.MSPRole_ADMIN:
nodeOU = msp.adminOU
case m.MSPRole_ORDERER:
nodeOU = msp.ordererOU
default:
return errors.New("Invalid MSPRoleType. It must be CLIENT, PEER, ADMIN or ORDERER")
}
if nodeOU == nil {
return errors.Errorf("cannot test for classification, node ou for type [%s], not defined, msp: [%s]", mspRole, msp.name)
}
for _, OU := range id.GetOrganizationalUnits() {
if OU.OrganizationalUnitIdentifier == nodeOU.OrganizationalUnitIdentifier {
return nil
}
}
return errors.Errorf("The identity does not contain OU [%s], MSP: [%s]", mspRole, msp.name)
}
// DeserializeIdentity returns an Identity given the byte-level
// representation of a SerializedIdentity struct
func (msp *bccspmsp) DeserializeIdentity(serializedID []byte) (Identity, error) {
mspLogger.Debug("Obtaining identity")
// We first deserialize to a SerializedIdentity to get the MSP ID
sId := &m.SerializedIdentity{}
err := proto.Unmarshal(serializedID, sId)
if err != nil {
return nil, errors.Wrap(err, "could not deserialize a SerializedIdentity")
}
if sId.Mspid != msp.name {
return nil, errors.Errorf("expected MSP ID %s, received %s", msp.name, sId.Mspid)
}
return msp.deserializeIdentityInternal(sId.IdBytes)
}
// deserializeIdentityInternal returns an identity given its byte-level representation
func (msp *bccspmsp) deserializeIdentityInternal(serializedIdentity []byte) (Identity, error) {
// This MSP will always deserialize certs this way
bl, _ := pem.Decode(serializedIdentity)
if bl == nil {
return nil, errors.New("could not decode the PEM structure")
}
cert, err := x509.ParseCertificate(bl.Bytes)
if err != nil {
return nil, errors.Wrap(err, "parseCertificate failed")
}
// Now we have the certificate; make sure that its fields
// (e.g. the Issuer.OU or the Subject.OU) match with the
// MSP id that this MSP has; otherwise it might be an attack
// TODO!
// We can't do it yet because there is no standardized way
// (yet) to encode the MSP ID into the x.509 body of a cert
pub, err := msp.bccsp.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
if err != nil {
return nil, errors.WithMessage(err, "failed to import certificate's public key")
}
return newIdentity(cert, pub, msp)
}
// SatisfiesPrincipal returns nil if the identity matches the principal or an error otherwise
func (msp *bccspmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal) error {
principals, err := collectPrincipals(principal, msp.GetVersion())
if err != nil {
return err
}
for _, principal := range principals {
err = msp.internalSatisfiesPrincipalInternalFunc(id, principal)
if err != nil {
return err
}
}
return nil
}
// collectPrincipals collects principals from combined principals into a single MSPPrincipal slice.
func collectPrincipals(principal *m.MSPPrincipal, mspVersion MSPVersion) ([]*m.MSPPrincipal, error) {
switch principal.PrincipalClassification {
case m.MSPPrincipal_COMBINED:
// Combined principals are not supported in MSP v1.0 or v1.1
if mspVersion <= MSPv1_1 {
return nil, errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
}
// Principal is a combination of multiple principals.
principals := &m.CombinedPrincipal{}
err := proto.Unmarshal(principal.Principal, principals)
if err != nil {
return nil, errors.Wrap(err, "could not unmarshal CombinedPrincipal from principal")
}
// Return an error if there are no principals in the combined principal.
if len(principals.Principals) == 0 {
return nil, errors.New("No principals in CombinedPrincipal")
}
// Recursively call msp.collectPrincipals for all combined principals.
// There is no limit for the levels of nesting for the combined principals.
var principalsSlice []*m.MSPPrincipal
for _, cp := range principals.Principals {
internalSlice, err := collectPrincipals(cp, mspVersion)
if err != nil {
return nil, err
}
principalsSlice = append(principalsSlice, internalSlice...)
}
// All the combined principals have been collected into principalsSlice
return principalsSlice, nil
default:
return []*m.MSPPrincipal{principal}, nil
}
}
// satisfiesPrincipalInternalPreV13 takes as arguments the identity and the principal.
// The function returns an error if one occurred.
// The function implements the behavior of an MSP up to and including v1.1.
func (msp *bccspmsp) satisfiesPrincipalInternalPreV13(id Identity, principal *m.MSPPrincipal) error {
switch principal.PrincipalClassification {
// in this case, we have to check whether the
// identity has a role in the msp - member or admin
case m.MSPPrincipal_ROLE:
// Principal contains the msp role
mspRole := &m.MSPRole{}
err := proto.Unmarshal(principal.Principal, mspRole)
if err != nil {
return errors.Wrap(err, "could not unmarshal MSPRole from principal")
}
// at first, we check whether the MSP
// identifier is the same as that of the identity
if mspRole.MspIdentifier != msp.name {
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", mspRole.MspIdentifier, id.GetMSPIdentifier())
}
// now we validate the different msp roles
switch mspRole.Role {
case m.MSPRole_MEMBER:
// in the case of member, we simply check
// whether this identity is valid for the MSP
mspLogger.Debugf("Checking if identity satisfies MEMBER role for %s", msp.name)
return msp.Validate(id)
case m.MSPRole_ADMIN:
mspLogger.Debugf("Checking if identity satisfies ADMIN role for %s", msp.name)
// in the case of admin, we check that the
// id is exactly one of our admins
if msp.isInAdmins(id.(*identity)) {
return nil
}
return errors.New("This identity is not an admin")
case m.MSPRole_CLIENT:
fallthrough
case m.MSPRole_PEER:
mspLogger.Debugf("Checking if identity satisfies role [%s] for %s", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
if err := msp.Validate(id); err != nil {
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
}
if err := msp.hasOURole(id, mspRole.Role); err != nil {
return errors.Wrapf(err, "The identity is not a [%s] under this MSP [%s]", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
}
return nil
default:
return errors.Errorf("invalid MSP role type %d", int32(mspRole.Role))
}
case m.MSPPrincipal_IDENTITY:
// in this case we have to deserialize the principal's identity
// and compare it byte-by-byte with our cert
principalId, err := msp.DeserializeIdentity(principal.Principal)
if err != nil {
return errors.WithMessage(err, "invalid identity principal, not a certificate")
}
if bytes.Equal(id.(*identity).cert.Raw, principalId.(*identity).cert.Raw) {
return principalId.Validate()
}
return errors.New("The identities do not match")
case m.MSPPrincipal_ORGANIZATION_UNIT:
// Principal contains the OrganizationUnit
OU := &m.OrganizationUnit{}
err := proto.Unmarshal(principal.Principal, OU)
if err != nil {
return errors.Wrap(err, "could not unmarshal OrganizationUnit from principal")
}
// at first, we check whether the MSP
// identifier is the same as that of the identity
if OU.MspIdentifier != msp.name {
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", OU.MspIdentifier, id.GetMSPIdentifier())
}
// we then check if the identity is valid with this MSP
// and fail if it is not
err = msp.Validate(id)
if err != nil {
return err
}
// now we check whether any of this identity's OUs match the requested one
for _, ou := range id.GetOrganizationalUnits() {
if ou.OrganizationalUnitIdentifier == OU.OrganizationalUnitIdentifier &&
bytes.Equal(ou.CertifiersIdentifier, OU.CertifiersIdentifier) {
return nil
}
}
// if we are here, no match was found, return an error
return errors.New("The identities do not match")
default:
return errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
}
}
// satisfiesPrincipalInternalV13 takes as arguments the identity and the principal.
// The function returns an error if one occurred.
// The function implements the additional behavior expected of an MSP starting from v1.3.
// For pre-v1.3 functionality, the function calls the satisfiesPrincipalInternalPreV13.
func (msp *bccspmsp) satisfiesPrincipalInternalV13(id Identity, principal *m.MSPPrincipal) error {
switch principal.PrincipalClassification {
case m.MSPPrincipal_COMBINED:
return errors.New("SatisfiesPrincipalInternal shall not be called with a CombinedPrincipal")
case m.MSPPrincipal_ANONYMITY:
anon := &m.MSPIdentityAnonymity{}
err := proto.Unmarshal(principal.Principal, anon)
if err != nil {
return errors.Wrap(err, "could not unmarshal MSPIdentityAnonymity from principal")
}
switch anon.AnonymityType {
case m.MSPIdentityAnonymity_ANONYMOUS:
return errors.New("Principal is anonymous, but X.509 MSP does not support anonymous identities")
case m.MSPIdentityAnonymity_NOMINAL:
return nil
default:
return errors.Errorf("Unknown principal anonymity type: %d", anon.AnonymityType)
}
default:
// Use the pre-v1.3 function to check other principal types
return msp.satisfiesPrincipalInternalPreV13(id, principal)
}
}
// satisfiesPrincipalInternalV142 takes as arguments the identity and the principal.
// The function returns an error if one occurred.
// The function implements the additional behavior expected of an MSP starting from v2.0.
// For v1.3 functionality, the function calls the satisfiesPrincipalInternalPreV13.
func (msp *bccspmsp) satisfiesPrincipalInternalV142(id Identity, principal *m.MSPPrincipal) error {
_, okay := id.(*identity)
if !okay {
return errors.New("invalid identity type, expected *identity")
}
switch principal.PrincipalClassification {
case m.MSPPrincipal_ROLE:
if !msp.ouEnforcement {
break
}
// Principal contains the msp role
mspRole := &m.MSPRole{}
err := proto.Unmarshal(principal.Principal, mspRole)
if err != nil {
return errors.Wrap(err, "could not unmarshal MSPRole from principal")
}
// at first, we check whether the MSP
// identifier is the same as that of the identity
if mspRole.MspIdentifier != msp.name {
return errors.Errorf("the identity is a member of a different MSP (expected %s, got %s)", mspRole.MspIdentifier, id.GetMSPIdentifier())
}
// now we validate the admin role only, the other roles are left to the v1.3 function
switch mspRole.Role {
case m.MSPRole_ADMIN:
mspLogger.Debugf("Checking if identity has been named explicitly as an admin for %s", msp.name)
// in the case of admin, we check that the
// id is exactly one of our admins
if msp.isInAdmins(id.(*identity)) {
return nil
}
// or it carries the Admin OU, in this case check that the identity is valid as well.
mspLogger.Debugf("Checking if identity carries the admin ou for %s", msp.name)
if err := msp.Validate(id); err != nil {
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
}
if err := msp.hasOURole(id, m.MSPRole_ADMIN); err != nil {
return errors.Wrapf(err, "The identity is not an admin under this MSP [%s]", msp.name)
}
return nil
case m.MSPRole_ORDERER:
mspLogger.Debugf("Checking if identity satisfies role [%s] for %s", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
if err := msp.Validate(id); err != nil {
return errors.Wrapf(err, "The identity is not valid under this MSP [%s]", msp.name)
}
if err := msp.hasOURole(id, mspRole.Role); err != nil {
return errors.Wrapf(err, "The identity is not a [%s] under this MSP [%s]", m.MSPRole_MSPRoleType_name[int32(mspRole.Role)], msp.name)
}
return nil
}
}
// Use the v1.3 function to check other principal types
return msp.satisfiesPrincipalInternalV13(id, principal)
}
func (msp *bccspmsp) isInAdmins(id *identity) bool {
for _, admincert := range msp.admins {
if bytes.Equal(id.cert.Raw, admincert.(*identity).cert.Raw) {
// we do not need to check whether the admin is a valid identity
// according to this MSP, since we already check this at Setup time
// if there is a match, we can just return
return true
}
}
return false
}
// getCertificationChain returns the certification chain of the passed identity within this msp
func (msp *bccspmsp) getCertificationChain(id Identity) ([]*x509.Certificate, error) {
mspLogger.Debugf("MSP %s getting certification chain", msp.name)
switch id := id.(type) {
// If this identity is of this specific type,
// this is how I can validate it given the
// root of trust this MSP has
case *identity:
return msp.getCertificationChainForBCCSPIdentity(id)
default:
return nil, errors.New("identity type not recognized")
}
}
// getCertificationChainForBCCSPIdentity returns the certification chain of the passed bccsp identity within this msp
func (msp *bccspmsp) getCertificationChainForBCCSPIdentity(id *identity) ([]*x509.Certificate, error) {
if id == nil {
return nil, errors.New("Invalid bccsp identity. Must be different from nil.")
}
// we expect to have a valid VerifyOptions instance
if msp.opts == nil {
return nil, errors.New("Invalid msp instance")
}
// CAs cannot be directly used as identities..
if id.cert.IsCA {
return nil, errors.New("An X509 certificate with Basic Constraint: " +
"Certificate Authority equals true cannot be used as an identity")
}
return msp.getValidationChain(id.cert, false)
}
func (msp *bccspmsp) getUniqueValidationChain(cert *x509.Certificate, opts x509.VerifyOptions) ([]*x509.Certificate, error) {
// ask golang to validate the cert for us based on the options that we've built at setup time
if msp.opts == nil {
return nil, errors.New("the supplied identity has no verify options")
}
validationChains, err := cert.Verify(opts)
if err != nil {
return nil, errors.WithMessage(err, "the supplied identity is not valid")
}
// we only support a single validation chain;
// if there's more than one then there might
// be unclarity about who owns the identity
if len(validationChains) != 1 {
return nil, errors.Errorf("this MSP only supports a single validation chain, got %d", len(validationChains))
}
// Make the additional verification checks that were done in Go 1.14.
err = verifyLegacyNameConstraints(validationChains[0])
if err != nil {
return nil, errors.WithMessage(err, "the supplied identity is not valid")
}
return validationChains[0], nil
}
var (
oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
oidExtensionNameConstraints = asn1.ObjectIdentifier{2, 5, 29, 30}
)
// verifyLegacyNameConstraints exercises the name constraint validation rules
// that were part of the certificate verification process in Go 1.14.
//
// If a signing certificate contains a name constraint, the leaf certificate
// does not include SAN extensions, and the leaf's common name looks like a
// host name, the validation would fail with an x509.CertificateInvalidError
// and a rason of x509.NameConstraintsWithoutSANs.
func verifyLegacyNameConstraints(chain []*x509.Certificate) error {
if len(chain) < 2 {
return nil
}
// Leaf certificates with SANs are fine.
if oidInExtensions(oidExtensionSubjectAltName, chain[0].Extensions) {
return nil
}
// Leaf certificates without a hostname in CN are fine.
if !validHostname(chain[0].Subject.CommonName) {
return nil
}
// If an intermediate or root have a name constraint, validation
// would fail in Go 1.14.
for _, c := range chain[1:] {
if oidInExtensions(oidExtensionNameConstraints, c.Extensions) {
return x509.CertificateInvalidError{Cert: chain[0], Reason: x509.NameConstraintsWithoutSANs}
}
}
return nil
}
func oidInExtensions(oid asn1.ObjectIdentifier, exts []pkix.Extension) bool {
for _, ext := range exts {
if ext.Id.Equal(oid) {
return true
}
}
return false
}
// validHostname reports whether host is a valid hostname that can be matched or
// matched against according to RFC 6125 2.2, with some leniency to accommodate
// legacy values.
//
// This implementation is sourced from the standard library.
func validHostname(host string) bool {
host = strings.TrimSuffix(host, ".")
if len(host) == 0 {
return false
}
for i, part := range strings.Split(host, ".") {
if part == "" {
// Empty label.
return false
}
if i == 0 && part == "*" {
// Only allow full left-most wildcards, as those are the only ones
// we match, and matching literal '*' characters is probably never
// the expected behavior.
continue
}
for j, c := range part {
if 'a' <= c && c <= 'z' {
continue
}
if '0' <= c && c <= '9' {
continue
}
if 'A' <= c && c <= 'Z' {
continue
}
if c == '-' && j != 0 {
continue
}
if c == '_' || c == ':' {
// Not valid characters in hostnames, but commonly
// found in deployments outside the WebPKI.
continue
}
return false
}
}
return true
}
func (msp *bccspmsp) getValidationChain(cert *x509.Certificate, isIntermediateChain bool) ([]*x509.Certificate, error) {
validationChain, err := msp.getUniqueValidationChain(cert, msp.getValidityOptsForCert(cert))
if err != nil {
return nil, errors.WithMessage(err, "failed getting validation chain")
}
// we expect a chain of length at least 2
if len(validationChain) < 2 {
return nil, errors.Errorf("expected a chain of length at least 2, got %d", len(validationChain))
}
// check that the parent is a leaf of the certification tree
// if validating an intermediate chain, the first certificate will the parent
parentPosition := 1
if isIntermediateChain {
parentPosition = 0
}
if msp.certificationTreeInternalNodesMap[string(validationChain[parentPosition].Raw)] {
return nil, errors.Errorf("invalid validation chain. Parent certificate should be a leaf of the certification tree [%v]", cert.Raw)
}
return validationChain, nil
}
// getCertificationChainIdentifier returns the certification chain identifier of the passed identity within this msp.
// The identifier is computes as the SHA256 of the concatenation of the certificates in the chain.
func (msp *bccspmsp) getCertificationChainIdentifier(id Identity) ([]byte, error) {
chain, err := msp.getCertificationChain(id)
if err != nil {
return nil, errors.WithMessagef(err, "failed getting certification chain for [%v]", id)
}
// chain[0] is the certificate representing the identity.
// It will be discarded
return msp.getCertificationChainIdentifierFromChain(chain[1:])
}
func (msp *bccspmsp) getCertificationChainIdentifierFromChain(chain []*x509.Certificate) ([]byte, error) {
// Hash the chain
// Use the hash of the identity's certificate as id in the IdentityIdentifier
hashOpt, err := bccsp.GetHashOpt(msp.cryptoConfig.IdentityIdentifierHashFunction)
if err != nil {
return nil, errors.WithMessage(err, "failed getting hash function options")
}
hf, err := msp.bccsp.GetHash(hashOpt)
if err != nil {
return nil, errors.WithMessage(err, "failed getting hash function when computing certification chain identifier")
}
for i := 0; i < len(chain); i++ {
hf.Write(chain[i].Raw)
}
return hf.Sum(nil), nil
}
// sanitizeCert ensures that x509 certificates signed using ECDSA
// do have signatures in Low-S. If this is not the case, the certificate
// is regenerated to have a Low-S signature.
func (msp *bccspmsp) sanitizeCert(cert *x509.Certificate) (*x509.Certificate, error) {
var err error
if isECDSASignedCert(cert) {
isRootCACert := false
validityOpts := msp.getValidityOptsForCert(cert)
if cert.IsCA && cert.CheckSignatureFrom(cert) == nil {
// this is a root CA we can already sanitize it
cert, err = sanitizeECDSASignedCert(cert, cert)
if err != nil {
return nil, err
}
isRootCACert = true
validityOpts.Roots = x509.NewCertPool()
validityOpts.Roots.AddCert(cert)
}
// Lookup for a parent certificate to perform the sanitization
// run cert validation at any rate, if this is a root CA
// we will validate already sanitized cert
chain, err := msp.getUniqueValidationChain(cert, validityOpts)
if err != nil {
return nil, err
}
// once we finish validation and this is already
// sanitized certificate, there is no need to
// sanitize it once again hence we can just return it
if isRootCACert {
return cert, nil
}
// ok, this is no a root CA cert, and now we
// have chain of certs and can extract parent
// to sanitize the cert whenever it's intermediate or leaf certificate
var parentCert *x509.Certificate
if len(chain) <= 1 {
return nil, fmt.Errorf("failed to traverse certificate verification chain"+
" for leaf or intermediate certificate, with subject %s", cert.Subject)
}
parentCert = chain[1]
// Sanitize
return sanitizeECDSASignedCert(cert, parentCert)
}
return cert, nil
}
// IsWellFormed checks if the given identity can be deserialized into its provider-specific form.
// In this MSP implementation, well formed means that the PEM has a Type which is either
// the string 'CERTIFICATE' or the Type is missing altogether.
func (msp *bccspmsp) IsWellFormed(identity *m.SerializedIdentity) error {
bl, rest := pem.Decode(identity.IdBytes)
if bl == nil {
return errors.New("PEM decoding resulted in an empty block")
}
if len(rest) > 0 {
return errors.Errorf("identity %s for MSP %s has trailing bytes", string(identity.IdBytes), identity.Mspid)
}
// Important: This method looks very similar to getCertFromPem(idBytes []byte) (*x509.Certificate, error)
// But we:
// 1) Must ensure PEM block is of type CERTIFICATE or is empty
// 2) Must not replace getCertFromPem with this method otherwise we will introduce
// a change in validation logic which will result in a chain fork.
if bl.Type != "CERTIFICATE" && bl.Type != "" {
return errors.Errorf("pem type is %s, should be 'CERTIFICATE' or missing", bl.Type)
}
cert, err := x509.ParseCertificate(bl.Bytes)
if err != nil {
return err
}
if !isECDSASignedCert(cert) {
return nil
}
return isIdentitySignedInCanonicalForm(cert.Signature, identity.Mspid, identity.IdBytes)
}
func isIdentitySignedInCanonicalForm(sig []byte, mspID string, pemEncodedIdentity []byte) error {
r, s, err := utils.UnmarshalECDSASignature(sig)
if err != nil {
return err
}
expectedSig, err := utils.MarshalECDSASignature(r, s)
if err != nil {
return err
}
if !bytes.Equal(expectedSig, sig) {
return errors.Errorf("identity %s for MSP %s has a non canonical signature",
string(pemEncodedIdentity), mspID)
}
return nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"bytes"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/bccsp/utils"
m "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
func (msp *bccspmsp) getCertifiersIdentifier(certRaw []byte) ([]byte, error) {
// 1. check that certificate is registered in msp.rootCerts or msp.intermediateCerts
cert, err := msp.getCertFromPem(certRaw)
if err != nil {
return nil, fmt.Errorf("Failed getting certificate for [%v]: [%s]", certRaw, err)
}
// 2. Sanitize it to ensure like for like comparison
cert, err = msp.sanitizeCert(cert)
if err != nil {
return nil, fmt.Errorf("sanitizeCert failed %s", err)
}
found := false
root := false
// Search among root certificates
for _, v := range msp.rootCerts {
if v.(*identity).cert.Equal(cert) {
found = true
root = true
break
}
}
if !found {
// Search among root intermediate certificates
for _, v := range msp.intermediateCerts {
if v.(*identity).cert.Equal(cert) {
found = true
break
}
}
}
if !found {
// Certificate not valid, reject configuration
return nil, fmt.Errorf("Failed adding OU. Certificate [%v] not in root or intermediate certs.", cert)
}
// 3. get the certification path for it
var certifiersIdentifier []byte
var chain []*x509.Certificate
if root {
chain = []*x509.Certificate{cert}
} else {
chain, err = msp.getValidationChain(cert, true)
if err != nil {
return nil, fmt.Errorf("Failed computing validation chain for [%v]. [%s]", cert, err)
}
}
// 4. compute the hash of the certification path
certifiersIdentifier, err = msp.getCertificationChainIdentifierFromChain(chain)
if err != nil {
return nil, fmt.Errorf("Failed computing Certifiers Identifier for [%v]. [%s]", certRaw, err)
}
return certifiersIdentifier, nil
}
func (msp *bccspmsp) setupCrypto(conf *m.FabricMSPConfig) error {
msp.cryptoConfig = conf.CryptoConfig
if msp.cryptoConfig == nil {
// Move to defaults
msp.cryptoConfig = &m.FabricCryptoConfig{
SignatureHashFamily: bccsp.SHA2,
IdentityIdentifierHashFunction: bccsp.SHA256,
}
mspLogger.Debugf("CryptoConfig was nil. Move to defaults.")
}
if msp.cryptoConfig.SignatureHashFamily == "" {
msp.cryptoConfig.SignatureHashFamily = bccsp.SHA2
mspLogger.Debugf("CryptoConfig.SignatureHashFamily was nil. Move to defaults.")
}
if msp.cryptoConfig.IdentityIdentifierHashFunction == "" {
msp.cryptoConfig.IdentityIdentifierHashFunction = bccsp.SHA256
mspLogger.Debugf("CryptoConfig.IdentityIdentifierHashFunction was nil. Move to defaults.")
}
msp.supportedPublicKeyAlgorithms = make(map[x509.PublicKeyAlgorithm]bool)
msp.supportedPublicKeyAlgorithms[x509.ECDSA] = true
return nil
}
func (msp *bccspmsp) setupCAs(conf *m.FabricMSPConfig) error {
// make and fill the set of CA certs - we expect them to be there
if len(conf.RootCerts) == 0 {
return errors.New("expected at least one CA certificate")
}
// pre-create the verify options with roots and intermediates.
// This is needed to make certificate sanitation working.
// Recall that sanitization is applied also to root CA and intermediate
// CA certificates. After their sanitization is done, the opts
// will be recreated using the sanitized certs.
msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
for _, v := range conf.RootCerts {
cert, err := msp.getCertFromPem(v)
if err != nil {
return err
}
msp.opts.Roots.AddCert(cert)
}
for _, v := range conf.IntermediateCerts {
cert, err := msp.getCertFromPem(v)
if err != nil {
return err
}
msp.opts.Intermediates.AddCert(cert)
}
// Load root and intermediate CA identities
// Recall that when an identity is created, its certificate gets sanitized
msp.rootCerts = make([]Identity, len(conf.RootCerts))
for i, trustedCert := range conf.RootCerts {
id, _, err := msp.getIdentityFromConf(trustedCert)
if err != nil {
return err
}
msp.rootCerts[i] = id
}
// make and fill the set of intermediate certs (if present)
msp.intermediateCerts = make([]Identity, len(conf.IntermediateCerts))
for i, trustedCert := range conf.IntermediateCerts {
id, _, err := msp.getIdentityFromConf(trustedCert)
if err != nil {
return err
}
msp.intermediateCerts[i] = id
}
// root CA and intermediate CA certificates are sanitized, they can be re-imported
msp.opts = &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
for _, id := range msp.rootCerts {
msp.opts.Roots.AddCert(id.(*identity).cert)
}
for _, id := range msp.intermediateCerts {
msp.opts.Intermediates.AddCert(id.(*identity).cert)
}
return nil
}
func (msp *bccspmsp) setupAdmins(conf *m.FabricMSPConfig) error {
return msp.internalSetupAdmin(conf)
}
func (msp *bccspmsp) setupAdminsPreV142(conf *m.FabricMSPConfig) error {
// make and fill the set of admin certs (if present)
msp.admins = make([]Identity, len(conf.Admins))
for i, admCert := range conf.Admins {
id, _, err := msp.getIdentityFromConf(admCert)
if err != nil {
return err
}
msp.admins[i] = id
}
return nil
}
func (msp *bccspmsp) setupAdminsV142(conf *m.FabricMSPConfig) error {
// make and fill the set of admin certs (if present)
if err := msp.setupAdminsPreV142(conf); err != nil {
return err
}
if len(msp.admins) == 0 && (!msp.ouEnforcement || msp.adminOU == nil) {
return errors.New("administrators must be declared when no admin ou classification is set")
}
return nil
}
func isECDSASignatureAlgorithm(algid asn1.ObjectIdentifier) bool {
// This is the set of ECDSA algorithms supported by Go 1.14 for CRL
// signatures.
ecdsaSignaureAlgorithms := []asn1.ObjectIdentifier{
{1, 2, 840, 10045, 4, 1}, // oidSignatureECDSAWithSHA1
{1, 2, 840, 10045, 4, 3, 2}, // oidSignatureECDSAWithSHA256
{1, 2, 840, 10045, 4, 3, 3}, // oidSignatureECDSAWithSHA384
{1, 2, 840, 10045, 4, 3, 4}, // oidSignatureECDSAWithSHA512
}
for _, id := range ecdsaSignaureAlgorithms {
if id.Equal(algid) {
return true
}
}
return false
}
func (msp *bccspmsp) setupCRLs(conf *m.FabricMSPConfig) error {
// setup the CRL (if present)
msp.CRL = make([]*pkix.CertificateList, len(conf.RevocationList))
for i, crlbytes := range conf.RevocationList {
crl, err := x509.ParseCRL(crlbytes)
if err != nil {
return errors.Wrap(err, "could not parse RevocationList")
}
// Massage the ECDSA signature values
if isECDSASignatureAlgorithm(crl.SignatureAlgorithm.Algorithm) {
r, s, err := utils.UnmarshalECDSASignature(crl.SignatureValue.RightAlign())
if err != nil {
return err
}
sig, err := utils.MarshalECDSASignature(r, s)
if err != nil {
return err
}
crl.SignatureValue = asn1.BitString{Bytes: sig, BitLength: 8 * len(sig)}
}
// TODO: pre-verify the signature on the CRL and create a map
// of CA certs to respective CRLs so that later upon
// validation we can already look up the CRL given the
// chain of the certificate to be validated
msp.CRL[i] = crl
}
return nil
}
func (msp *bccspmsp) finalizeSetupCAs() error {
// ensure that our CAs are properly formed and that they are valid
for _, id := range append(append([]Identity{}, msp.rootCerts...), msp.intermediateCerts...) {
if !id.(*identity).cert.IsCA {
return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", id.(*identity).cert.SerialNumber)
}
if _, err := getSubjectKeyIdentifierFromCert(id.(*identity).cert); err != nil {
return errors.WithMessagef(err, "CA Certificate problem with Subject Key Identifier extension, (SN: %x)", id.(*identity).cert.SerialNumber)
}
if err := msp.validateCAIdentity(id.(*identity)); err != nil {
return errors.WithMessagef(err, "CA Certificate is not valid, (SN: %s)", id.(*identity).cert.SerialNumber)
}
}
// populate certificationTreeInternalNodesMap to mark the internal nodes of the
// certification tree
msp.certificationTreeInternalNodesMap = make(map[string]bool)
for _, id := range append([]Identity{}, msp.intermediateCerts...) {
chain, err := msp.getUniqueValidationChain(id.(*identity).cert, msp.getValidityOptsForCert(id.(*identity).cert))
if err != nil {
return errors.WithMessagef(err, "failed getting validation chain, (SN: %s)", id.(*identity).cert.SerialNumber)
}
// Recall chain[0] is id.(*identity).id so it does not count as a parent
for i := 1; i < len(chain); i++ {
msp.certificationTreeInternalNodesMap[string(chain[i].Raw)] = true
}
}
return nil
}
func (msp *bccspmsp) setupNodeOUs(config *m.FabricMSPConfig) error {
if config.FabricNodeOus != nil {
msp.ouEnforcement = config.FabricNodeOus.Enable
if config.FabricNodeOus.ClientOuIdentifier == nil || len(config.FabricNodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier) == 0 {
return errors.New("Failed setting up NodeOUs. ClientOU must be different from nil.")
}
if config.FabricNodeOus.PeerOuIdentifier == nil || len(config.FabricNodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier) == 0 {
return errors.New("Failed setting up NodeOUs. PeerOU must be different from nil.")
}
// ClientOU
msp.clientOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.ClientOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.ClientOuIdentifier.Certificate)
if err != nil {
return err
}
msp.clientOU.CertifiersIdentifier = certifiersIdentifier
}
// PeerOU
msp.peerOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.PeerOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.PeerOuIdentifier.Certificate)
if err != nil {
return err
}
msp.peerOU.CertifiersIdentifier = certifiersIdentifier
}
} else {
msp.ouEnforcement = false
}
return nil
}
func (msp *bccspmsp) setupNodeOUsV142(config *m.FabricMSPConfig) error {
if config.FabricNodeOus == nil {
msp.ouEnforcement = false
return nil
}
msp.ouEnforcement = config.FabricNodeOus.Enable
counter := 0
// ClientOU
if config.FabricNodeOus.ClientOuIdentifier != nil {
msp.clientOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.ClientOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.ClientOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.ClientOuIdentifier.Certificate)
if err != nil {
return err
}
msp.clientOU.CertifiersIdentifier = certifiersIdentifier
}
counter++
} else {
msp.clientOU = nil
}
// PeerOU
if config.FabricNodeOus.PeerOuIdentifier != nil {
msp.peerOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.PeerOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.PeerOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.PeerOuIdentifier.Certificate)
if err != nil {
return err
}
msp.peerOU.CertifiersIdentifier = certifiersIdentifier
}
counter++
} else {
msp.peerOU = nil
}
// AdminOU
if config.FabricNodeOus.AdminOuIdentifier != nil {
msp.adminOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.AdminOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.AdminOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.AdminOuIdentifier.Certificate)
if err != nil {
return err
}
msp.adminOU.CertifiersIdentifier = certifiersIdentifier
}
counter++
} else {
msp.adminOU = nil
}
// OrdererOU
if config.FabricNodeOus.OrdererOuIdentifier != nil {
msp.ordererOU = &OUIdentifier{OrganizationalUnitIdentifier: config.FabricNodeOus.OrdererOuIdentifier.OrganizationalUnitIdentifier}
if len(config.FabricNodeOus.OrdererOuIdentifier.Certificate) != 0 {
certifiersIdentifier, err := msp.getCertifiersIdentifier(config.FabricNodeOus.OrdererOuIdentifier.Certificate)
if err != nil {
return err
}
msp.ordererOU.CertifiersIdentifier = certifiersIdentifier
}
counter++
} else {
msp.ordererOU = nil
}
if counter == 0 {
// Disable NodeOU
msp.ouEnforcement = false
}
return nil
}
func (msp *bccspmsp) setupSigningIdentity(conf *m.FabricMSPConfig) error {
if conf.SigningIdentity != nil {
sid, err := msp.getSigningIdentityFromConf(conf.SigningIdentity)
if err != nil {
return err
}
expirationTime := sid.ExpiresAt()
now := time.Now()
if expirationTime.After(now) {
mspLogger.Debug("Signing identity expires at", expirationTime)
} else if expirationTime.IsZero() {
mspLogger.Debug("Signing identity has no known expiration time")
} else {
return errors.Errorf("signing identity expired %v ago", now.Sub(expirationTime))
}
msp.signer = sid
}
return nil
}
func (msp *bccspmsp) setupOUs(conf *m.FabricMSPConfig) error {
msp.ouIdentifiers = make(map[string][][]byte)
for _, ou := range conf.OrganizationalUnitIdentifiers {
certifiersIdentifier, err := msp.getCertifiersIdentifier(ou.Certificate)
if err != nil {
return errors.WithMessagef(err, "failed getting certificate for [%v]", ou)
}
// Check for duplicates
found := false
for _, id := range msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] {
if bytes.Equal(id, certifiersIdentifier) {
mspLogger.Warningf("Duplicate found in ou identifiers [%s, %v]", ou.OrganizationalUnitIdentifier, id)
found = true
break
}
}
if !found {
// No duplicates found, add it
msp.ouIdentifiers[ou.OrganizationalUnitIdentifier] = append(
msp.ouIdentifiers[ou.OrganizationalUnitIdentifier],
certifiersIdentifier,
)
}
}
return nil
}
func (msp *bccspmsp) setupTLSCAs(conf *m.FabricMSPConfig) error {
opts := &x509.VerifyOptions{Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool()}
// Load TLS root and intermediate CA identities
msp.tlsRootCerts = make([][]byte, len(conf.TlsRootCerts))
rootCerts := make([]*x509.Certificate, len(conf.TlsRootCerts))
for i, trustedCert := range conf.TlsRootCerts {
cert, err := msp.getCertFromPem(trustedCert)
if err != nil {
return err
}
rootCerts[i] = cert
msp.tlsRootCerts[i] = trustedCert
opts.Roots.AddCert(cert)
}
// make and fill the set of intermediate certs (if present)
msp.tlsIntermediateCerts = make([][]byte, len(conf.TlsIntermediateCerts))
intermediateCerts := make([]*x509.Certificate, len(conf.TlsIntermediateCerts))
for i, trustedCert := range conf.TlsIntermediateCerts {
cert, err := msp.getCertFromPem(trustedCert)
if err != nil {
return err
}
intermediateCerts[i] = cert
msp.tlsIntermediateCerts[i] = trustedCert
opts.Intermediates.AddCert(cert)
}
// ensure that our CAs are properly formed and that they are valid
for _, cert := range append(append([]*x509.Certificate{}, rootCerts...), intermediateCerts...) {
if cert == nil {
continue
}
if !cert.IsCA {
return errors.Errorf("CA Certificate did not have the CA attribute, (SN: %x)", cert.SerialNumber)
}
if _, err := getSubjectKeyIdentifierFromCert(cert); err != nil {
return errors.WithMessagef(err, "CA Certificate problem with Subject Key Identifier extension, (SN: %x)", cert.SerialNumber)
}
opts.CurrentTime = cert.NotBefore.Add(time.Second)
if err := msp.validateTLSCAIdentity(cert, opts); err != nil {
return errors.WithMessagef(err, "CA Certificate is not valid, (SN: %s)", cert.SerialNumber)
}
}
return nil
}
func (msp *bccspmsp) setupV1(conf1 *m.FabricMSPConfig) error {
err := msp.preSetupV1(conf1)
if err != nil {
return err
}
err = msp.postSetupV1(conf1)
if err != nil {
return err
}
return nil
}
func (msp *bccspmsp) preSetupV1(conf *m.FabricMSPConfig) error {
// setup crypto config
if err := msp.setupCrypto(conf); err != nil {
return err
}
// Setup CAs
if err := msp.setupCAs(conf); err != nil {
return err
}
// Setup Admins
if err := msp.setupAdmins(conf); err != nil {
return err
}
// Setup CRLs
if err := msp.setupCRLs(conf); err != nil {
return err
}
// Finalize setup of the CAs
if err := msp.finalizeSetupCAs(); err != nil {
return err
}
// setup the signer (if present)
if err := msp.setupSigningIdentity(conf); err != nil {
return err
}
// setup TLS CAs
if err := msp.setupTLSCAs(conf); err != nil {
return err
}
// setup the OUs
if err := msp.setupOUs(conf); err != nil {
return err
}
return nil
}
func (msp *bccspmsp) preSetupV142(conf *m.FabricMSPConfig) error {
// setup crypto config
if err := msp.setupCrypto(conf); err != nil {
return err
}
// Setup CAs
if err := msp.setupCAs(conf); err != nil {
return err
}
// Setup CRLs
if err := msp.setupCRLs(conf); err != nil {
return err
}
// Finalize setup of the CAs
if err := msp.finalizeSetupCAs(); err != nil {
return err
}
// setup the signer (if present)
if err := msp.setupSigningIdentity(conf); err != nil {
return err
}
// setup TLS CAs
if err := msp.setupTLSCAs(conf); err != nil {
return err
}
// setup the OUs
if err := msp.setupOUs(conf); err != nil {
return err
}
// setup NodeOUs
if err := msp.setupNodeOUsV142(conf); err != nil {
return err
}
// Setup Admins
if err := msp.setupAdmins(conf); err != nil {
return err
}
return nil
}
func (msp *bccspmsp) postSetupV1(conf *m.FabricMSPConfig) error {
// make sure that admins are valid members as well
// this way, when we validate an admin MSP principal
// we can simply check for exact match of certs
for i, admin := range msp.admins {
err := admin.Validate()
if err != nil {
return errors.WithMessagef(err, "admin %d is invalid", i)
}
}
return nil
}
func (msp *bccspmsp) setupV11(conf *m.FabricMSPConfig) error {
err := msp.preSetupV1(conf)
if err != nil {
return err
}
// setup NodeOUs
if err := msp.setupNodeOUs(conf); err != nil {
return err
}
err = msp.postSetupV11(conf)
if err != nil {
return err
}
return nil
}
func (msp *bccspmsp) setupV142(conf *m.FabricMSPConfig) error {
err := msp.preSetupV142(conf)
if err != nil {
return err
}
err = msp.postSetupV142(conf)
if err != nil {
return err
}
return nil
}
func (msp *bccspmsp) setupV3(conf *m.FabricMSPConfig) error {
err := msp.preSetupV142(conf)
if err != nil {
return err
}
msp.supportedPublicKeyAlgorithms[x509.Ed25519] = true
err = msp.postSetupV142(conf)
if err != nil {
return err
}
return nil
}
func (msp *bccspmsp) postSetupV11(conf *m.FabricMSPConfig) error {
// Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required. Call post setup as per V1
return msp.postSetupV1(conf)
}
// Check that admins are clients
principalBytes, err := proto.Marshal(&m.MSPRole{Role: m.MSPRole_CLIENT, MspIdentifier: msp.name})
if err != nil {
return errors.Wrapf(err, "failed creating MSPRole_CLIENT")
}
principal := &m.MSPPrincipal{
PrincipalClassification: m.MSPPrincipal_ROLE,
Principal: principalBytes,
}
for i, admin := range msp.admins {
err = admin.SatisfiesPrincipal(principal)
if err != nil {
return errors.WithMessagef(err, "admin %d is invalid", i)
}
}
return nil
}
func (msp *bccspmsp) postSetupV142(conf *m.FabricMSPConfig) error {
// Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required. Call post setup as per V1
return msp.postSetupV1(conf)
}
// Check that admins are clients or admins
for i, admin := range msp.admins {
err1 := msp.hasOURole(admin, m.MSPRole_CLIENT)
err2 := msp.hasOURole(admin, m.MSPRole_ADMIN)
if err1 != nil && err2 != nil {
return errors.Errorf("admin %d is invalid [%s,%s]", i, err1, err2)
}
}
return nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"bytes"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"math/big"
"reflect"
"time"
"github.com/pkg/errors"
)
func (msp *bccspmsp) validateIdentity(id *identity) error {
id.validationMutex.Lock()
defer id.validationMutex.Unlock()
// return cached validation value if already validated
if id.validated {
return id.validationErr
}
id.validated = true
if !msp.supportedPublicKeyAlgorithms[id.cert.PublicKeyAlgorithm] {
err := errors.Errorf("%s is not supported", id.cert.PublicKeyAlgorithm.String())
id.validationErr = errors.WithMessage(err, "could not validate identity's public key algorithm")
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d) Unsupported public key algorithm: %s", id.validationErr, id.cert.Subject.CommonName, id.cert.Issuer.CommonName, id.cert.SerialNumber, id.cert.PublicKeyAlgorithm)
return id.validationErr
}
validationChain, err := msp.getCertificationChainForBCCSPIdentity(id)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not obtain certification chain")
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
return id.validationErr
}
err = msp.validateIdentityAgainstChain(id, validationChain)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not validate identity against certification chain")
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
return id.validationErr
}
err = msp.internalValidateIdentityOusFunc(id)
if err != nil {
id.validationErr = errors.WithMessage(err, "could not validate identity's OUs")
mspLogger.Warnf("Could not validate identity: %s (certificate subject=%s issuer=%s serialnumber=%d)", id.validationErr, id.cert.Subject, id.cert.Issuer, id.cert.SerialNumber)
return id.validationErr
}
return nil
}
func (msp *bccspmsp) validateCAIdentity(id *identity) error {
if !id.cert.IsCA {
return errors.New("Only CA identities can be validated")
}
validationChain, err := msp.getUniqueValidationChain(id.cert, msp.getValidityOptsForCert(id.cert))
if err != nil {
return errors.WithMessage(err, "could not obtain certification chain")
}
if len(validationChain) == 1 {
// validationChain[0] is the root CA certificate
return nil
}
return msp.validateIdentityAgainstChain(id, validationChain)
}
func (msp *bccspmsp) validateTLSCAIdentity(cert *x509.Certificate, opts *x509.VerifyOptions) error {
if !cert.IsCA {
return errors.New("Only CA identities can be validated")
}
validationChain, err := msp.getUniqueValidationChain(cert, *opts)
if err != nil {
return errors.WithMessage(err, "could not obtain certification chain")
}
if len(validationChain) == 1 {
// validationChain[0] is the root CA certificate
return nil
}
return msp.validateCertAgainstChain(cert, validationChain)
}
func (msp *bccspmsp) validateIdentityAgainstChain(id *identity, validationChain []*x509.Certificate) error {
return msp.validateCertAgainstChain(id.cert, validationChain)
}
func (msp *bccspmsp) validateCertAgainstChain(cert *x509.Certificate, validationChain []*x509.Certificate) error {
// here we know that the identity is valid; now we have to check whether it has been revoked
// identify the SKI of the CA that signed this cert
SKI, err := getSubjectKeyIdentifierFromCert(validationChain[1])
if err != nil {
return errors.WithMessage(err, "could not obtain Subject Key Identifier for signer cert")
}
// check whether one of the CRLs we have has this cert's
// SKI as its AuthorityKeyIdentifier
for _, crl := range msp.CRL {
aki, err := getAuthorityKeyIdentifierFromCrl(crl)
if err != nil {
return errors.WithMessage(err, "could not obtain Authority Key Identifier for crl")
}
// check if the SKI of the cert that signed us matches the AKI of any of the CRLs
if bytes.Equal(aki, SKI) {
// we have a CRL, check whether the serial number is revoked
for _, rc := range crl.TBSCertList.RevokedCertificates {
if rc.SerialNumber.Cmp(cert.SerialNumber) == 0 {
// We have found a CRL whose AKI matches the SKI of
// the CA (root or intermediate) that signed the
// certificate that is under validation. As a
// precaution, we verify that said CA is also the
// signer of this CRL.
err = validationChain[1].CheckCRLSignature(crl)
if err != nil {
// the CA cert that signed the certificate
// that is under validation did not sign the
// candidate CRL - skip
mspLogger.Warningf("Invalid signature over the identified CRL, error %+v", err)
continue
}
// A CRL also includes a time of revocation so that
// the CA can say "this cert is to be revoked starting
// from this time"; however here we just assume that
// revocation applies instantaneously from the time
// the MSP config is committed and used so we will not
// make use of that field
return errors.New("The certificate has been revoked")
}
}
}
}
return nil
}
func (msp *bccspmsp) validateIdentityOUsV1(id *identity) error {
// Check that the identity's OUs are compatible with those recognized by this MSP,
// meaning that the intersection is not empty.
if len(msp.ouIdentifiers) > 0 {
found := false
for _, OU := range id.GetOrganizationalUnits() {
certificationIDs, exists := msp.ouIdentifiers[OU.OrganizationalUnitIdentifier]
if exists {
for _, certificationID := range certificationIDs {
if bytes.Equal(certificationID, OU.CertifiersIdentifier) {
found = true
break
}
}
}
}
if !found {
if len(id.GetOrganizationalUnits()) == 0 {
return errors.New("the identity certificate does not contain an Organizational Unit (OU)")
}
return errors.Errorf("none of the identity's organizational units %s are in MSP %s", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
}
return nil
}
func (msp *bccspmsp) validateIdentityOUsV11(id *identity) error {
// Run the same checks as per V1
err := msp.validateIdentityOUsV1(id)
if err != nil {
return err
}
// Perform V1_1 additional checks:
//
// -- Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required
return nil
}
// Make sure that the identity has only one of the special OUs
// used to tell apart clients or peers.
counter := 0
for _, OU := range id.GetOrganizationalUnits() {
// Is OU.OrganizationalUnitIdentifier one of the special OUs?
var nodeOU *OUIdentifier
switch OU.OrganizationalUnitIdentifier {
case msp.clientOU.OrganizationalUnitIdentifier:
nodeOU = msp.clientOU
case msp.peerOU.OrganizationalUnitIdentifier:
nodeOU = msp.peerOU
default:
continue
}
// Yes. Then, enforce the certifiers identifier is this is specified.
// It is not specified, it means that any certification path is fine.
if len(nodeOU.CertifiersIdentifier) != 0 && !bytes.Equal(nodeOU.CertifiersIdentifier, OU.CertifiersIdentifier) {
return errors.Errorf("certifiersIdentifier does not match: %v, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
counter++
if counter > 1 {
break
}
}
// the identity should have exactly one OU role, return an error if the counter is not 1.
if counter == 0 {
return errors.Errorf("the identity does not have an OU that resolves to client or peer. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
if counter > 1 {
return errors.Errorf("the identity must be a client or a peer identity to be valid, not a combination of them. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
return nil
}
func (msp *bccspmsp) validateIdentityOUsV142(id *identity) error {
// Run the same checks as per V1
err := msp.validateIdentityOUsV1(id)
if err != nil {
return err
}
// -- Check for OU enforcement
if !msp.ouEnforcement {
// No enforcement required
return nil
}
// Make sure that the identity has only one of the special OUs
// used to tell apart clients, peers and admins.
counter := 0
validOUs := make(map[string]*OUIdentifier)
if msp.clientOU != nil {
validOUs[msp.clientOU.OrganizationalUnitIdentifier] = msp.clientOU
}
if msp.peerOU != nil {
validOUs[msp.peerOU.OrganizationalUnitIdentifier] = msp.peerOU
}
if msp.adminOU != nil {
validOUs[msp.adminOU.OrganizationalUnitIdentifier] = msp.adminOU
}
if msp.ordererOU != nil {
validOUs[msp.ordererOU.OrganizationalUnitIdentifier] = msp.ordererOU
}
for _, OU := range id.GetOrganizationalUnits() {
// Is OU.OrganizationalUnitIdentifier one of the special OUs?
nodeOU := validOUs[OU.OrganizationalUnitIdentifier]
if nodeOU == nil {
continue
}
// Yes. Then, enforce the certifiers identifier in this is specified.
// If is not specified, it means that any certification path is fine.
if len(nodeOU.CertifiersIdentifier) != 0 && !bytes.Equal(nodeOU.CertifiersIdentifier, OU.CertifiersIdentifier) {
return errors.Errorf("certifiersIdentifier does not match: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
counter++
if counter > 1 {
break
}
}
// the identity should have exactly one OU role, return an error if the counter is not 1.
if counter == 0 {
return errors.Errorf("the identity does not have an OU that resolves to client, peer, orderer, or admin role. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
if counter > 1 {
return errors.Errorf("the identity must have a client, a peer, an orderer, or an admin OU role to be valid, not a combination of them. OUs: %s, MSP: [%s]", OUIDs(id.GetOrganizationalUnits()), msp.name)
}
return nil
}
func (msp *bccspmsp) getValidityOptsForCert(cert *x509.Certificate) x509.VerifyOptions {
// First copy the opts to override the CurrentTime field
// in order to make the certificate passing the expiration test
// independently from the real local current time.
// This is a temporary workaround for FAB-3678
var tempOpts x509.VerifyOptions
tempOpts.Roots = msp.opts.Roots
tempOpts.DNSName = msp.opts.DNSName
tempOpts.Intermediates = msp.opts.Intermediates
tempOpts.KeyUsages = msp.opts.KeyUsages
tempOpts.CurrentTime = cert.NotBefore.Add(time.Second)
return tempOpts
}
/*
This is the definition of the ASN.1 marshalling of AuthorityKeyIdentifier
from https://www.ietf.org/rfc/rfc5280.txt
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
KeyIdentifier ::= OCTET STRING
CertificateSerialNumber ::= INTEGER
*/
type authorityKeyIdentifier struct {
KeyIdentifier []byte `asn1:"optional,tag:0"`
AuthorityCertIssuer []byte `asn1:"optional,tag:1"`
AuthorityCertSerialNumber big.Int `asn1:"optional,tag:2"`
}
// getAuthorityKeyIdentifierFromCrl returns the Authority Key Identifier
// for the supplied CRL. The authority key identifier can be used to identify
// the public key corresponding to the private key which was used to sign the CRL.
func getAuthorityKeyIdentifierFromCrl(crl *pkix.CertificateList) ([]byte, error) {
aki := authorityKeyIdentifier{}
for _, ext := range crl.TBSCertList.Extensions {
// Authority Key Identifier is identified by the following ASN.1 tag
// authorityKeyIdentifier (2 5 29 35) (see https://tools.ietf.org/html/rfc3280.html)
if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 35}) {
_, err := asn1.Unmarshal(ext.Value, &aki)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal AKI")
}
return aki.KeyIdentifier, nil
}
}
return nil, errors.New("authorityKeyIdentifier not found in certificate")
}
// getSubjectKeyIdentifierFromCert returns the Subject Key Identifier for the supplied certificate
// Subject Key Identifier is an identifier of the public key of this certificate
func getSubjectKeyIdentifierFromCert(cert *x509.Certificate) ([]byte, error) {
var SKI []byte
for _, ext := range cert.Extensions {
// Subject Key Identifier is identified by the following ASN.1 tag
// subjectKeyIdentifier (2 5 29 14) (see https://tools.ietf.org/html/rfc3280.html)
if reflect.DeepEqual(ext.Id, asn1.ObjectIdentifier{2, 5, 29, 14}) {
_, err := asn1.Unmarshal(ext.Value, &SKI)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal Subject Key Identifier")
}
return SKI, nil
}
}
return nil, errors.New("subjectKeyIdentifier not found in certificate")
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package msp
import (
"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
var mspLogger = flogging.MustGetLogger("msp")
type mspManagerImpl struct {
// map that contains all MSPs that we have setup or otherwise added
mspsMap map[string]MSP
// map that maps MSPs by their provider types
mspsByProviders map[ProviderType][]MSP
// error that might have occurred at startup
up bool
}
// NewMSPManager returns a new MSP manager instance;
// note that this instance is not initialized until
// the Setup method is called
func NewMSPManager() MSPManager {
return &mspManagerImpl{}
}
// Setup initializes the internal data structures of this manager and creates MSPs
func (mgr *mspManagerImpl) Setup(msps []MSP) error {
if mgr.up {
mspLogger.Infof("MSP manager already up")
return nil
}
mspLogger.Debugf("Setting up the MSP manager (%d msps)", len(msps))
// create the map that assigns MSP IDs to their manager instance - once
mgr.mspsMap = make(map[string]MSP)
// create the map that sorts MSPs by their provider types
mgr.mspsByProviders = make(map[ProviderType][]MSP)
for _, msp := range msps {
// add the MSP to the map of active MSPs
mspID, err := msp.GetIdentifier()
if err != nil {
return errors.WithMessage(err, "could not extract msp identifier")
}
mgr.mspsMap[mspID] = msp
providerType := msp.GetType()
mgr.mspsByProviders[providerType] = append(mgr.mspsByProviders[providerType], msp)
}
mgr.up = true
mspLogger.Debugf("MSP manager setup complete, setup %d msps", len(msps))
return nil
}
// GetMSPs returns the MSPs that are managed by this manager
func (mgr *mspManagerImpl) GetMSPs() (map[string]MSP, error) {
return mgr.mspsMap, nil
}
// DeserializeIdentity returns an identity given its serialized version supplied as argument
func (mgr *mspManagerImpl) DeserializeIdentity(serializedID []byte) (Identity, error) {
if !mgr.up {
return nil, errors.New("channel doesn't exist")
}
// We first deserialize to a SerializedIdentity to get the MSP ID
sId := &msp.SerializedIdentity{}
err := proto.Unmarshal(serializedID, sId)
if err != nil {
return nil, errors.Wrap(err, "could not deserialize a SerializedIdentity")
}
// we can now attempt to obtain the MSP
msp := mgr.mspsMap[sId.Mspid]
if msp == nil {
return nil, errors.Errorf("MSP %s is not defined on channel", sId.Mspid)
}
switch t := msp.(type) {
case *bccspmsp:
return t.deserializeIdentityInternal(sId.IdBytes)
case *idemixMSPWrapper:
return t.deserializeIdentityInternal(sId.IdBytes)
default:
return t.DeserializeIdentity(serializedID)
}
}
func (mgr *mspManagerImpl) IsWellFormed(identity *msp.SerializedIdentity) error {
// Iterate over all the MSPs by their providers, and find at least 1 MSP that can attest
// that this identity is well formed
for _, mspList := range mgr.mspsByProviders {
// We are guaranteed to have at least 1 MSP in each list from the initialization at Setup()
msp := mspList[0]
if err := msp.IsWellFormed(identity); err == nil {
return nil
}
}
return errors.New("no MSP provider recognizes the identity")
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"bytes"
"crypto/sha256"
"encoding/asn1"
"encoding/base64"
"fmt"
"math/big"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/hyperledger/fabric/common/util"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// NewBlock constructs a block with no data and no metadata.
func NewBlock(seqNum uint64, previousHash []byte) *cb.Block {
block := &cb.Block{}
block.Header = &cb.BlockHeader{}
block.Header.Number = seqNum
block.Header.PreviousHash = previousHash
block.Header.DataHash = []byte{}
block.Data = &cb.BlockData{}
var metadataContents [][]byte
for i := 0; i < len(cb.BlockMetadataIndex_name); i++ {
metadataContents = append(metadataContents, []byte{})
}
block.Metadata = &cb.BlockMetadata{Metadata: metadataContents}
return block
}
type asn1Header struct {
Number *big.Int
PreviousHash []byte
DataHash []byte
}
func BlockHeaderBytes(b *cb.BlockHeader) []byte {
asn1Header := asn1Header{
PreviousHash: b.PreviousHash,
DataHash: b.DataHash,
Number: new(big.Int).SetUint64(b.Number),
}
result, err := asn1.Marshal(asn1Header)
if err != nil {
// Errors should only arise for types which cannot be encoded, since the
// BlockHeader type is known a-priori to contain only encodable types, an
// error here is fatal and should not be propagated
panic(err)
}
return result
}
func BlockHeaderHash(b *cb.BlockHeader) []byte {
sum := sha256.Sum256(BlockHeaderBytes(b))
return sum[:]
}
func BlockDataHash(b *cb.BlockData) ([]byte, error) {
if err := VerifyTransactionsAreWellFormed(b); err != nil {
return nil, err
}
return ComputeBlockDataHash(b), nil
}
func ComputeBlockDataHash(b *cb.BlockData) []byte {
sum := sha256.Sum256(bytes.Join(b.Data, nil))
return sum[:]
}
// GetChannelIDFromBlockBytes returns channel ID given byte array which represents
// the block
func GetChannelIDFromBlockBytes(bytes []byte) (string, error) {
block, err := UnmarshalBlock(bytes)
if err != nil {
return "", err
}
return GetChannelIDFromBlock(block)
}
// GetChannelIDFromBlock returns channel ID in the block
func GetChannelIDFromBlock(block *cb.Block) (string, error) {
if block == nil || block.Data == nil || block.Data.Data == nil || len(block.Data.Data) == 0 {
return "", errors.New("failed to retrieve channel id - block is empty")
}
var err error
envelope, err := GetEnvelopeFromBlock(block.Data.Data[0])
if err != nil {
return "", err
}
payload, err := UnmarshalPayload(envelope.Payload)
if err != nil {
return "", err
}
if payload.Header == nil {
return "", errors.New("failed to retrieve channel id - payload header is empty")
}
chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return "", err
}
return chdr.ChannelId, nil
}
// GetMetadataFromBlock retrieves metadata at the specified index.
func GetMetadataFromBlock(block *cb.Block, index cb.BlockMetadataIndex) (*cb.Metadata, error) {
if block.Metadata == nil {
return nil, errors.New("no metadata in block")
}
if len(block.Metadata.Metadata) <= int(index) {
return nil, errors.Errorf("no metadata at index [%s]", index)
}
md := &cb.Metadata{}
err := proto.Unmarshal(block.Metadata.Metadata[index], md)
if err != nil {
return nil, errors.Wrapf(err, "error unmarshalling metadata at index [%s]", index)
}
return md, nil
}
// GetMetadataFromBlockOrPanic retrieves metadata at the specified index, or
// panics on error
func GetMetadataFromBlockOrPanic(block *cb.Block, index cb.BlockMetadataIndex) *cb.Metadata {
md, err := GetMetadataFromBlock(block, index)
if err != nil {
panic(err)
}
return md
}
// GetConsenterMetadataFromBlock attempts to retrieve consenter metadata from the value
// stored in block metadata at index SIGNATURES (first field). If no consenter metadata
// is found there, it falls back to index ORDERER (third field).
func GetConsenterMetadataFromBlock(block *cb.Block) (*cb.Metadata, error) {
m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)
if err != nil {
return nil, errors.WithMessage(err, "failed to retrieve metadata")
}
// TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer
if len(m.Value) == 0 {
return GetMetadataFromBlock(block, cb.BlockMetadataIndex_ORDERER)
}
obm := &cb.OrdererBlockMetadata{}
err = proto.Unmarshal(m.Value, obm)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal orderer block metadata")
}
res := &cb.Metadata{}
err = proto.Unmarshal(obm.ConsenterMetadata, res)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal consenter metadata")
}
return res, nil
}
// GetLastConfigIndexFromBlock retrieves the index of the last config block as
// encoded in the block metadata
func GetLastConfigIndexFromBlock(block *cb.Block) (uint64, error) {
m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES)
if err != nil {
return 0, errors.WithMessage(err, "failed to retrieve metadata")
}
// TODO FAB-15864 Remove this fallback when we can stop supporting upgrade from pre-1.4.1 orderer
if len(m.Value) == 0 {
m, err := GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG)
if err != nil {
return 0, errors.WithMessage(err, "failed to retrieve metadata")
}
lc := &cb.LastConfig{}
err = proto.Unmarshal(m.Value, lc)
if err != nil {
return 0, errors.Wrap(err, "error unmarshalling LastConfig")
}
return lc.Index, nil
}
obm := &cb.OrdererBlockMetadata{}
err = proto.Unmarshal(m.Value, obm)
if err != nil {
return 0, errors.Wrap(err, "failed to unmarshal orderer block metadata")
}
return obm.LastConfig.Index, nil
}
// GetLastConfigIndexFromBlockOrPanic retrieves the index of the last config
// block as encoded in the block metadata, or panics on error
func GetLastConfigIndexFromBlockOrPanic(block *cb.Block) uint64 {
index, err := GetLastConfigIndexFromBlock(block)
if err != nil {
panic(err)
}
return index
}
// CopyBlockMetadata copies metadata from one block into another
func CopyBlockMetadata(src *cb.Block, dst *cb.Block) {
dst.Metadata = src.Metadata
// Once copied initialize with rest of the
// required metadata positions.
InitBlockMetadata(dst)
}
// InitBlockMetadata initializes metadata structure
func InitBlockMetadata(block *cb.Block) {
if block.Metadata == nil {
block.Metadata = &cb.BlockMetadata{Metadata: [][]byte{{}, {}, {}, {}, {}}}
} else if len(block.Metadata.Metadata) < int(cb.BlockMetadataIndex_COMMIT_HASH+1) {
for i := len(block.Metadata.Metadata); i <= int(cb.BlockMetadataIndex_COMMIT_HASH); i++ {
block.Metadata.Metadata = append(block.Metadata.Metadata, []byte{})
}
}
}
type VerifierBuilder func(block *cb.Block) BlockVerifierFunc
type BlockVerifierFunc func(header *cb.BlockHeader, metadata *cb.BlockMetadata) error
//go:generate counterfeiter -o mocks/policy.go --fake-name Policy . policy
type policy interface { // copied from common.policies to avoid circular import.
// EvaluateSignedData takes a set of SignedData and evaluates whether
// 1) the signatures are valid over the related message
// 2) the signing identities satisfy the policy
EvaluateSignedData(signatureSet []*SignedData) error
}
func BlockSignatureVerifier(bftEnabled bool, consenters []*cb.Consenter, policy policy) BlockVerifierFunc {
return func(header *cb.BlockHeader, metadata *cb.BlockMetadata) error {
if len(metadata.GetMetadata()) < int(cb.BlockMetadataIndex_SIGNATURES)+1 {
return errors.Errorf("no signatures in block metadata")
}
md := &cb.Metadata{}
if err := proto.Unmarshal(metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], md); err != nil {
return errors.Wrapf(err, "error unmarshalling signatures from metadata: %v", err)
}
var signatureSet []*SignedData
for _, metadataSignature := range md.Signatures {
var signerIdentity []byte
var signedPayload []byte
// if the SignatureHeader is empty and the IdentifierHeader is present, then the consenter expects us to fetch its identity by its numeric identifier
if bftEnabled && len(metadataSignature.GetSignatureHeader()) == 0 && len(metadataSignature.GetIdentifierHeader()) > 0 {
identifierHeader, err := UnmarshalIdentifierHeader(metadataSignature.IdentifierHeader)
if err != nil {
return fmt.Errorf("failed unmarshalling identifier header for block %d: %v", header.GetNumber(), err)
}
identifier := identifierHeader.GetIdentifier()
signerIdentity = searchConsenterIdentityByID(consenters, identifier)
if len(signerIdentity) == 0 {
// The identifier is not within the consenter set
continue
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.IdentifierHeader, BlockHeaderBytes(header))
} else {
signatureHeader, err := UnmarshalSignatureHeader(metadataSignature.GetSignatureHeader())
if err != nil {
return fmt.Errorf("failed unmarshalling signature header for block %d: %v", header.GetNumber(), err)
}
signedPayload = util.ConcatenateBytes(md.Value, metadataSignature.SignatureHeader, BlockHeaderBytes(header))
signerIdentity = signatureHeader.Creator
}
signatureSet = append(
signatureSet,
&SignedData{
Identity: signerIdentity,
Data: signedPayload,
Signature: metadataSignature.Signature,
},
)
}
return policy.EvaluateSignedData(signatureSet)
}
}
func searchConsenterIdentityByID(consenters []*cb.Consenter, identifier uint32) []byte {
for _, consenter := range consenters {
if consenter.Id == identifier {
return MarshalOrPanic(&msp.SerializedIdentity{
Mspid: consenter.MspId,
IdBytes: consenter.Identity,
})
}
}
return nil
}
func VerifyTransactionsAreWellFormed(bd *cb.BlockData) error {
if bd == nil || bd.Data == nil || len(bd.Data) == 0 {
return errors.New("empty block")
}
// If we have a single transaction, and the block is a config block, then no need to check
// well formed-ness, because there cannot be another transaction in the original block.
if HasConfigTx(bd) {
return nil
}
for i, rawTx := range bd.Data {
env := &cb.Envelope{}
if err := proto.Unmarshal(rawTx, env); err != nil {
return fmt.Errorf("transaction %d is invalid: %v", i, err)
}
if len(env.Payload) == 0 {
return fmt.Errorf("transaction %d has no payload", i)
}
if len(env.Signature) == 0 {
return fmt.Errorf("transaction %d has no signature", i)
}
expected, err := proto.Marshal(env)
if err != nil {
return fmt.Errorf("failed re-marshaling envelope: %v", err)
}
if len(expected) < len(rawTx) {
return fmt.Errorf("transaction %d has %d trailing bytes", i, len(rawTx)-len(expected))
}
if !bytes.Equal(expected, rawTx) {
return fmt.Errorf("transaction %d (%s) does not match its raw form (%s)", i,
base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))
}
}
return nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"crypto/rand"
"fmt"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/internal/pkg/identity"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
// MarshalOrPanic serializes a protobuf message and panics if this
// operation fails
func MarshalOrPanic(pb proto.Message) []byte {
if !pb.ProtoReflect().IsValid() {
panic(errors.New("proto: Marshal called with nil"))
}
data, err := proto.Marshal(pb)
if err != nil {
panic(err)
}
return data
}
// Marshal serializes a protobuf message.
func Marshal(pb proto.Message) ([]byte, error) {
if !pb.ProtoReflect().IsValid() {
return nil, errors.New("proto: Marshal called with nil")
}
return proto.Marshal(pb)
}
// CreateNonceOrPanic generates a nonce using the common/crypto package
// and panics if this operation fails.
func CreateNonceOrPanic() []byte {
nonce, err := CreateNonce()
if err != nil {
panic(err)
}
return nonce
}
// CreateNonce generates a nonce using the common/crypto package.
func CreateNonce() ([]byte, error) {
nonce, err := getRandomNonce()
return nonce, errors.WithMessage(err, "error generating random nonce")
}
// UnmarshalEnvelopeOfType unmarshals an envelope of the specified type,
// including unmarshalling the payload data
func UnmarshalEnvelopeOfType(envelope *cb.Envelope, headerType cb.HeaderType, message proto.Message) (*cb.ChannelHeader, error) {
payload, err := UnmarshalPayload(envelope.Payload)
if err != nil {
return nil, err
}
if payload.Header == nil {
return nil, errors.New("envelope must have a Header")
}
chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return nil, err
}
if chdr.Type != int32(headerType) {
return nil, errors.Errorf("invalid type %s, expected %s", cb.HeaderType(chdr.Type), headerType)
}
err = proto.Unmarshal(payload.Data, message)
err = errors.Wrapf(err, "error unmarshalling message for type %s", headerType)
return chdr, err
}
// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block
// and unmarshals it -- it panics if either of these operations fail
func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {
envelope, err := ExtractEnvelope(block, index)
if err != nil {
panic(err)
}
return envelope
}
// ExtractEnvelope retrieves the requested envelope from a given block and
// unmarshals it
func ExtractEnvelope(block *cb.Block, index int) (*cb.Envelope, error) {
if block.Data == nil {
return nil, errors.New("block data is nil")
}
envelopeCount := len(block.Data.Data)
if index < 0 || index >= envelopeCount {
return nil, errors.New("envelope index out of bounds")
}
marshaledEnvelope := block.Data.Data[index]
envelope, err := GetEnvelopeFromBlock(marshaledEnvelope)
err = errors.WithMessagef(err, "block data does not carry an envelope at index %d", index)
return envelope, err
}
// MakeChannelHeader creates a ChannelHeader.
func MakeChannelHeader(headerType cb.HeaderType, version int32, chainID string, epoch uint64) *cb.ChannelHeader {
tm := timestamppb.Now()
tm.Nanos = 0
return &cb.ChannelHeader{
Type: int32(headerType),
Version: version,
Timestamp: tm,
ChannelId: chainID,
Epoch: epoch,
}
}
// MakeSignatureHeader creates a SignatureHeader.
func MakeSignatureHeader(serializedCreatorCertChain []byte, nonce []byte) *cb.SignatureHeader {
return &cb.SignatureHeader{
Creator: serializedCreatorCertChain,
Nonce: nonce,
}
}
// SetTxID generates a transaction id based on the provided signature header
// and sets the TxId field in the channel header
func SetTxID(channelHeader *cb.ChannelHeader, signatureHeader *cb.SignatureHeader) {
channelHeader.TxId = ComputeTxID(
signatureHeader.Nonce,
signatureHeader.Creator,
)
}
// MakePayloadHeader creates a Payload Header.
func MakePayloadHeader(ch *cb.ChannelHeader, sh *cb.SignatureHeader) *cb.Header {
return &cb.Header{
ChannelHeader: MarshalOrPanic(ch),
SignatureHeader: MarshalOrPanic(sh),
}
}
// NewSignatureHeader returns a SignatureHeader with a valid nonce.
func NewSignatureHeader(id identity.Serializer) (*cb.SignatureHeader, error) {
creator, err := id.Serialize()
if err != nil {
return nil, err
}
nonce, err := CreateNonce()
if err != nil {
return nil, err
}
return &cb.SignatureHeader{
Creator: creator,
Nonce: nonce,
}, nil
}
// NewSignatureHeaderOrPanic returns a signature header and panics on error.
func NewSignatureHeaderOrPanic(id identity.Serializer) *cb.SignatureHeader {
if id == nil {
panic(errors.New("invalid signer. cannot be nil"))
}
signatureHeader, err := NewSignatureHeader(id)
if err != nil {
panic(fmt.Errorf("failed generating a new SignatureHeader: %s", err))
}
return signatureHeader
}
// SignOrPanic signs a message and panics on error.
func SignOrPanic(signer identity.Signer, msg []byte) []byte {
if signer == nil {
panic(errors.New("invalid signer. cannot be nil"))
}
sigma, err := signer.Sign(msg)
if err != nil {
panic(fmt.Errorf("failed generating signature: %s", err))
}
return sigma
}
// IsConfigBlock validates whenever given block contains configuration
// update transaction
func IsConfigBlock(block *cb.Block) bool {
if block.Data == nil {
return false
}
return HasConfigTx(block.Data)
}
func HasConfigTx(blockdata *cb.BlockData) bool {
if blockdata.Data == nil {
return false
}
if len(blockdata.Data) != 1 {
return false
}
marshaledEnvelope := blockdata.Data[0]
envelope, err := GetEnvelopeFromBlock(marshaledEnvelope)
if err != nil {
return false
}
payload, err := UnmarshalPayload(envelope.Payload)
if err != nil {
return false
}
if payload.Header == nil {
return false
}
hdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return false
}
return cb.HeaderType(hdr.Type) == cb.HeaderType_CONFIG
}
// ChannelHeader returns the *cb.ChannelHeader for a given *cb.Envelope.
func ChannelHeader(env *cb.Envelope) (*cb.ChannelHeader, error) {
if env == nil {
return nil, errors.New("Invalid envelope payload. can't be nil")
}
envPayload, err := UnmarshalPayload(env.Payload)
if err != nil {
return nil, err
}
if envPayload.Header == nil {
return nil, errors.New("header not set")
}
if envPayload.Header.ChannelHeader == nil {
return nil, errors.New("channel header not set")
}
chdr, err := UnmarshalChannelHeader(envPayload.Header.ChannelHeader)
if err != nil {
return nil, errors.WithMessage(err, "error unmarshalling channel header")
}
return chdr, nil
}
// ChannelID returns the Channel ID for a given *cb.Envelope.
func ChannelID(env *cb.Envelope) (string, error) {
chdr, err := ChannelHeader(env)
if err != nil {
return "", errors.WithMessage(err, "error retrieving channel header")
}
return chdr.ChannelId, nil
}
// EnvelopeToConfigUpdate is used to extract a ConfigUpdateEnvelope from an envelope of
// type CONFIG_UPDATE
func EnvelopeToConfigUpdate(configtx *cb.Envelope) (*cb.ConfigUpdateEnvelope, error) {
configUpdateEnv := &cb.ConfigUpdateEnvelope{}
_, err := UnmarshalEnvelopeOfType(configtx, cb.HeaderType_CONFIG_UPDATE, configUpdateEnv)
if err != nil {
return nil, err
}
return configUpdateEnv, nil
}
func getRandomNonce() ([]byte, error) {
key := make([]byte, 24)
_, err := rand.Read(key)
if err != nil {
return nil, errors.Wrap(err, "error getting random bytes")
}
return key, nil
}
func IsConfigTransaction(envelope *cb.Envelope) bool {
payload, err := UnmarshalPayload(envelope.Payload)
if err != nil {
return false
}
if payload.Header == nil {
return false
}
hdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return false
}
return cb.HeaderType(hdr.Type) == cb.HeaderType_CONFIG || cb.HeaderType(hdr.Type) == cb.HeaderType_ORDERER_TRANSACTION
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import "github.com/hyperledger/fabric-protos-go-apiv2/common"
func NewConfigGroup() *common.ConfigGroup {
return &common.ConfigGroup{
Groups: make(map[string]*common.ConfigGroup),
Values: make(map[string]*common.ConfigValue),
Policies: make(map[string]*common.ConfigPolicy),
}
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"crypto/sha256"
"encoding/hex"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
// CreateChaincodeProposal creates a proposal from given input.
// It returns the proposal and the transaction id associated to the proposal
func CreateChaincodeProposal(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
return CreateChaincodeProposalWithTransient(typ, channelID, cis, creator, nil)
}
// CreateChaincodeProposalWithTransient creates a proposal from given input
// It returns the proposal and the transaction id associated to the proposal
func CreateChaincodeProposalWithTransient(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
// generate a random nonce
nonce, err := getRandomNonce()
if err != nil {
return nil, "", err
}
// compute txid
txid := ComputeTxID(nonce, creator)
return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap)
}
// CreateChaincodeProposalWithTxIDAndTransient creates a proposal from given
// input. It returns the proposal and the transaction id associated with the
// proposal
func CreateChaincodeProposalWithTxIDAndTransient(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte, txid string, transientMap map[string][]byte) (*peer.Proposal, string, error) {
// generate a random nonce
nonce, err := getRandomNonce()
if err != nil {
return nil, "", err
}
// compute txid unless provided by tests
if txid == "" {
txid = ComputeTxID(nonce, creator)
}
return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap)
}
// CreateChaincodeProposalWithTxIDNonceAndTransient creates a proposal from
// given input
func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) {
ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId}
ccHdrExtBytes, err := proto.Marshal(ccHdrExt)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension")
}
cisBytes, err := proto.Marshal(cis)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec")
}
ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap}
ccPropPayloadBytes, err := proto.Marshal(ccPropPayload)
if err != nil {
return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
}
// TODO: epoch is now set to zero. This must be changed once we
// get a more appropriate mechanism to handle it in.
var epoch uint64
hdr := &common.Header{
ChannelHeader: MarshalOrPanic(
&common.ChannelHeader{
Type: int32(typ),
TxId: txid,
Timestamp: timestamppb.Now(),
ChannelId: channelID,
Extension: ccHdrExtBytes,
Epoch: epoch,
},
),
SignatureHeader: MarshalOrPanic(
&common.SignatureHeader{
Nonce: nonce,
Creator: creator,
},
),
}
hdrBytes, err := proto.Marshal(hdr)
if err != nil {
return nil, "", err
}
prop := &peer.Proposal{
Header: hdrBytes,
Payload: ccPropPayloadBytes,
}
return prop, txid, nil
}
// GetBytesProposalResponsePayload gets proposal response payload
func GetBytesProposalResponsePayload(hash []byte, response *peer.Response, result []byte, event []byte, ccid *peer.ChaincodeID) ([]byte, error) {
cAct := &peer.ChaincodeAction{
Events: event, Results: result,
Response: response,
ChaincodeId: ccid,
}
cActBytes, err := proto.Marshal(cAct)
if err != nil {
return nil, errors.Wrap(err, "error marshaling ChaincodeAction")
}
prp := &peer.ProposalResponsePayload{
Extension: cActBytes,
ProposalHash: hash,
}
prpBytes, err := proto.Marshal(prp)
return prpBytes, errors.Wrap(err, "error marshaling ProposalResponsePayload")
}
// GetBytesChaincodeProposalPayload gets the chaincode proposal payload
func GetBytesChaincodeProposalPayload(cpp *peer.ChaincodeProposalPayload) ([]byte, error) {
if cpp == nil {
return nil, errors.New("error marshaling ChaincodeProposalPayload: proto: Marshal called with nil")
}
cppBytes, err := proto.Marshal(cpp)
return cppBytes, errors.Wrap(err, "error marshaling ChaincodeProposalPayload")
}
// GetBytesResponse gets the bytes of Response
func GetBytesResponse(res *peer.Response) ([]byte, error) {
if res == nil {
return nil, errors.New("error marshaling Response: proto: Marshal called with nil")
}
resBytes, err := proto.Marshal(res)
return resBytes, errors.Wrap(err, "error marshaling Response")
}
// GetBytesChaincodeEvent gets the bytes of ChaincodeEvent
func GetBytesChaincodeEvent(event *peer.ChaincodeEvent) ([]byte, error) {
if event == nil {
return nil, errors.New("error marshaling ChaincodeEvent: proto: Marshal called with nil")
}
eventBytes, err := proto.Marshal(event)
return eventBytes, errors.Wrap(err, "error marshaling ChaincodeEvent")
}
// GetBytesChaincodeActionPayload get the bytes of ChaincodeActionPayload from
// the message
func GetBytesChaincodeActionPayload(cap *peer.ChaincodeActionPayload) ([]byte, error) {
if cap == nil {
return nil, errors.New("error marshaling ChaincodeActionPayload: proto: Marshal called with nil")
}
capBytes, err := proto.Marshal(cap)
return capBytes, errors.Wrap(err, "error marshaling ChaincodeActionPayload")
}
// GetBytesProposalResponse gets proposal bytes response
func GetBytesProposalResponse(pr *peer.ProposalResponse) ([]byte, error) {
if pr == nil {
return nil, errors.New("error marshaling ProposalResponse: proto: Marshal called with nil")
}
respBytes, err := proto.Marshal(pr)
return respBytes, errors.Wrap(err, "error marshaling ProposalResponse")
}
// GetBytesHeader get the bytes of Header from the message
func GetBytesHeader(hdr *common.Header) ([]byte, error) {
if hdr == nil {
return nil, errors.New("error marshaling Header: proto: Marshal called with nil")
}
bytes, err := proto.Marshal(hdr)
return bytes, errors.Wrap(err, "error marshaling Header")
}
// GetBytesSignatureHeader get the bytes of SignatureHeader from the message
func GetBytesSignatureHeader(hdr *common.SignatureHeader) ([]byte, error) {
if hdr == nil {
return nil, errors.New("error marshaling SignatureHeader: proto: Marshal called with nil")
}
bytes, err := proto.Marshal(hdr)
return bytes, errors.Wrap(err, "error marshaling SignatureHeader")
}
// GetBytesTransaction get the bytes of Transaction from the message
func GetBytesTransaction(tx *peer.Transaction) ([]byte, error) {
if tx == nil {
return nil, errors.New("error marshaling Transaction: proto: Marshal called with nil")
}
bytes, err := proto.Marshal(tx)
return bytes, errors.Wrap(err, "error unmarshalling Transaction")
}
// GetBytesPayload get the bytes of Payload from the message
func GetBytesPayload(payl *common.Payload) ([]byte, error) {
if payl == nil {
return nil, errors.New("error marshaling Payload: proto: Marshal called with nil")
}
bytes, err := proto.Marshal(payl)
return bytes, errors.Wrap(err, "error marshaling Payload")
}
// GetBytesEnvelope get the bytes of Envelope from the message
func GetBytesEnvelope(env *common.Envelope) ([]byte, error) {
if env == nil {
return nil, errors.New("error marshaling Envelope: proto: Marshal called with nil")
}
bytes, err := proto.Marshal(env)
return bytes, errors.Wrap(err, "error marshaling Envelope")
}
// GetActionFromEnvelope extracts a ChaincodeAction message from a
// serialized Envelope
// TODO: fix function name as per FAB-11831
func GetActionFromEnvelope(envBytes []byte) (*peer.ChaincodeAction, error) {
env, err := GetEnvelopeFromBlock(envBytes)
if err != nil {
return nil, err
}
return GetActionFromEnvelopeMsg(env)
}
func GetActionFromEnvelopeMsg(env *common.Envelope) (*peer.ChaincodeAction, error) {
payl, err := UnmarshalPayload(env.Payload)
if err != nil {
return nil, err
}
tx, err := UnmarshalTransaction(payl.Data)
if err != nil {
return nil, err
}
if len(tx.Actions) == 0 {
return nil, errors.New("at least one TransactionAction required")
}
_, respPayload, err := GetPayloads(tx.Actions[0])
return respPayload, err
}
// CreateProposalFromCISAndTxid returns a proposal given a serialized identity
// and a ChaincodeInvocationSpec
func CreateProposalFromCISAndTxid(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
nonce, err := getRandomNonce()
if err != nil {
return nil, "", err
}
return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, nil)
}
// CreateProposalFromCIS returns a proposal given a serialized identity and a
// ChaincodeInvocationSpec
func CreateProposalFromCIS(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) {
return CreateChaincodeProposal(typ, channelID, cis, creator)
}
// CreateGetChaincodesProposal returns a GETCHAINCODES proposal given a
// serialized identity
func CreateGetChaincodesProposal(channelID string, creator []byte) (*peer.Proposal, string, error) {
ccinp := &peer.ChaincodeInput{Args: [][]byte{[]byte("getchaincodes")}}
lsccSpec := &peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Type: peer.ChaincodeSpec_GOLANG,
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
Input: ccinp,
},
}
return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, channelID, lsccSpec, creator)
}
// CreateGetInstalledChaincodesProposal returns a GETINSTALLEDCHAINCODES
// proposal given a serialized identity
func CreateGetInstalledChaincodesProposal(creator []byte) (*peer.Proposal, string, error) {
ccinp := &peer.ChaincodeInput{Args: [][]byte{[]byte("getinstalledchaincodes")}}
lsccSpec := &peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Type: peer.ChaincodeSpec_GOLANG,
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
Input: ccinp,
},
}
return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, "", lsccSpec, creator)
}
// CreateInstallProposalFromCDS returns a install proposal given a serialized
// identity and a ChaincodeDeploymentSpec
func CreateInstallProposalFromCDS(ccpack proto.Message, creator []byte) (*peer.Proposal, string, error) {
return createProposalFromCDS("", ccpack, creator, "install")
}
// CreateDeployProposalFromCDS returns a deploy proposal given a serialized
// identity and a ChaincodeDeploymentSpec
func CreateDeployProposalFromCDS(
channelID string,
cds *peer.ChaincodeDeploymentSpec,
creator []byte,
policy []byte,
escc []byte,
vscc []byte,
collectionConfig []byte) (*peer.Proposal, string, error) {
if collectionConfig == nil {
return createProposalFromCDS(channelID, cds, creator, "deploy", policy, escc, vscc)
}
return createProposalFromCDS(channelID, cds, creator, "deploy", policy, escc, vscc, collectionConfig)
}
// CreateUpgradeProposalFromCDS returns a upgrade proposal given a serialized
// identity and a ChaincodeDeploymentSpec
func CreateUpgradeProposalFromCDS(
channelID string,
cds *peer.ChaincodeDeploymentSpec,
creator []byte,
policy []byte,
escc []byte,
vscc []byte,
collectionConfig []byte) (*peer.Proposal, string, error) {
if collectionConfig == nil {
return createProposalFromCDS(channelID, cds, creator, "upgrade", policy, escc, vscc)
}
return createProposalFromCDS(channelID, cds, creator, "upgrade", policy, escc, vscc, collectionConfig)
}
// createProposalFromCDS returns a deploy or upgrade proposal given a
// serialized identity and a ChaincodeDeploymentSpec
func createProposalFromCDS(channelID string, msg proto.Message, creator []byte, propType string, args ...[]byte) (*peer.Proposal, string, error) {
// in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates.
var ccinp *peer.ChaincodeInput
var b []byte
var err error
if msg != nil {
if !msg.ProtoReflect().IsValid() {
return nil, "", errors.New("proto: Marshal called with nil")
}
b, err = proto.Marshal(msg)
if err != nil {
return nil, "", err
}
}
switch propType {
case "deploy":
fallthrough
case "upgrade":
cds, ok := msg.(*peer.ChaincodeDeploymentSpec)
if !ok || cds == nil {
return nil, "", errors.New("invalid message for creating lifecycle chaincode proposal")
}
Args := [][]byte{[]byte(propType), []byte(channelID), b}
Args = append(Args, args...)
ccinp = &peer.ChaincodeInput{Args: Args}
case "install":
ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}}
}
// wrap the deployment in an invocation spec to lscc...
lsccSpec := &peer.ChaincodeInvocationSpec{
ChaincodeSpec: &peer.ChaincodeSpec{
Type: peer.ChaincodeSpec_GOLANG,
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
Input: ccinp,
},
}
// ...and get the proposal for it
return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, channelID, lsccSpec, creator)
}
// ComputeTxID computes TxID as the Hash computed
// over the concatenation of nonce and creator.
func ComputeTxID(nonce, creator []byte) string {
// TODO: Get the Hash function to be used from
// channel configuration
hasher := sha256.New()
hasher.Write(nonce)
hasher.Write(creator)
return hex.EncodeToString(hasher.Sum(nil))
}
// CheckTxID checks that txid is equal to the Hash computed
// over the concatenation of nonce and creator.
func CheckTxID(txid string, nonce, creator []byte) error {
computedTxID := ComputeTxID(nonce, creator)
if txid != computedTxID {
return errors.Errorf("invalid txid. got [%s], expected [%s]", txid, computedTxID)
}
return nil
}
// InvokedChaincodeName takes the proposal bytes of a SignedProposal, and unpacks it all the way down,
// until either an error is encountered, or the chaincode name is found. This is useful primarily
// for chaincodes which wish to know the chaincode name originally invoked, in order to deny cc2cc
// invocations (or, perhaps to deny direct invocations and require cc2cc).
func InvokedChaincodeName(proposalBytes []byte) (string, error) {
proposal := &peer.Proposal{}
err := proto.Unmarshal(proposalBytes, proposal)
if err != nil {
return "", errors.WithMessage(err, "could not unmarshal proposal")
}
proposalPayload := &peer.ChaincodeProposalPayload{}
err = proto.Unmarshal(proposal.Payload, proposalPayload)
if err != nil {
return "", errors.WithMessage(err, "could not unmarshal chaincode proposal payload")
}
cis := &peer.ChaincodeInvocationSpec{}
err = proto.Unmarshal(proposalPayload.Input, cis)
if err != nil {
return "", errors.WithMessage(err, "could not unmarshal chaincode invocation spec")
}
if cis.ChaincodeSpec == nil {
return "", errors.Errorf("chaincode spec is nil")
}
if cis.ChaincodeSpec.ChaincodeId == nil {
return "", errors.Errorf("chaincode id is nil")
}
return cis.ChaincodeSpec.ChaincodeId.Name, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"bytes"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"strings"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"google.golang.org/protobuf/proto"
)
// SignedData is used to represent the general triplet required to verify a signature
// This is intended to be generic across crypto schemes, while most crypto schemes will
// include the signing identity and a nonce within the Data, this is left to the crypto
// implementation.
type SignedData struct {
Data []byte
Identity []byte
Signature []byte
}
// ConfigUpdateEnvelopeAsSignedData returns the set of signatures for the
// ConfigUpdateEnvelope as SignedData or an error indicating why this was not
// possible.
func ConfigUpdateEnvelopeAsSignedData(ce *common.ConfigUpdateEnvelope) ([]*SignedData, error) {
if ce == nil {
return nil, errors.New("No signatures for nil SignedConfigItem")
}
result := make([]*SignedData, len(ce.Signatures))
for i, configSig := range ce.Signatures {
sigHeader := &common.SignatureHeader{}
err := proto.Unmarshal(configSig.SignatureHeader, sigHeader)
if err != nil {
return nil, err
}
result[i] = &SignedData{
Data: bytes.Join([][]byte{configSig.SignatureHeader, ce.ConfigUpdate}, nil),
Identity: sigHeader.Creator,
Signature: configSig.Signature,
}
}
return result, nil
}
// EnvelopeAsSignedData returns the signatures for the Envelope as SignedData
// slice of length 1 or an error indicating why this was not possible.
func EnvelopeAsSignedData(env *common.Envelope) ([]*SignedData, error) {
if env == nil {
return nil, errors.New("No signatures for nil Envelope")
}
payload := &common.Payload{}
err := proto.Unmarshal(env.Payload, payload)
if err != nil {
return nil, err
}
if payload.Header == nil /* || payload.Header.SignatureHeader == nil */ {
return nil, errors.New("Missing Header")
}
shdr := &common.SignatureHeader{}
err = proto.Unmarshal(payload.Header.SignatureHeader, shdr)
if err != nil {
return nil, fmt.Errorf("GetSignatureHeaderFromBytes failed, err %s", err)
}
return []*SignedData{{
Data: env.Payload,
Identity: shdr.Creator,
Signature: env.Signature,
}}, nil
}
// LogMessageForSerializedIdentity returns a string with serialized identity information,
// or a string indicating why the serialized identity information cannot be returned.
// Any errors are intentionally returned in the return strings so that the function can be used in single-line log messages with minimal clutter.
func LogMessageForSerializedIdentity(serializedIdentity []byte) string {
id := &msp.SerializedIdentity{}
err := proto.Unmarshal(serializedIdentity, id)
if err != nil {
return fmt.Sprintf("Could not unmarshal serialized identity: %s", err)
}
pemBlock, _ := pem.Decode(id.IdBytes)
if pemBlock == nil {
// not all identities are certificates so simply log the serialized
// identity bytes
return fmt.Sprintf("serialized-identity=%x", serializedIdentity)
}
cert, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
return fmt.Sprintf("Could not parse certificate: %s", err)
}
return fmt.Sprintf("(mspid=%s subject=%s issuer=%s serialnumber=%d)", id.Mspid, cert.Subject, cert.Issuer, cert.SerialNumber)
}
func LogMessageForSerializedIdentities(signedData []*SignedData) (logMsg string) {
var identityMessages []string
for _, sd := range signedData {
identityMessages = append(identityMessages, LogMessageForSerializedIdentity(sd.Identity))
}
return strings.Join(identityMessages, ", ")
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"bytes"
"crypto/sha256"
b64 "encoding/base64"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// GetPayloads gets the underlying payload objects in a TransactionAction
func GetPayloads(txActions *peer.TransactionAction) (*peer.ChaincodeActionPayload, *peer.ChaincodeAction, error) {
// TODO: pass in the tx type (in what follows we're assuming the
// type is ENDORSER_TRANSACTION)
ccPayload, err := UnmarshalChaincodeActionPayload(txActions.Payload)
if err != nil {
return nil, nil, err
}
if ccPayload.Action == nil || ccPayload.Action.ProposalResponsePayload == nil {
return nil, nil, errors.New("no payload in ChaincodeActionPayload")
}
pRespPayload, err := UnmarshalProposalResponsePayload(ccPayload.Action.ProposalResponsePayload)
if err != nil {
return nil, nil, err
}
if pRespPayload.Extension == nil {
return nil, nil, errors.New("response payload is missing extension")
}
respPayload, err := UnmarshalChaincodeAction(pRespPayload.Extension)
if err != nil {
return ccPayload, nil, err
}
return ccPayload, respPayload, nil
}
// GetEnvelopeFromBlock gets an envelope from a block's Data field.
func GetEnvelopeFromBlock(data []byte) (*common.Envelope, error) {
// Block always begins with an envelope
var err error
env := &common.Envelope{}
if err = proto.Unmarshal(data, env); err != nil {
return nil, errors.Wrap(err, "error unmarshalling Envelope")
}
return env, nil
}
// CreateSignedEnvelope creates a signed envelope of the desired type, with
// marshaled dataMsg and signs it
func CreateSignedEnvelope(
txType common.HeaderType,
channelID string,
signer Signer,
dataMsg proto.Message,
msgVersion int32,
epoch uint64,
) (*common.Envelope, error) {
return CreateSignedEnvelopeWithTLSBinding(txType, channelID, signer, dataMsg, msgVersion, epoch, nil)
}
// CreateSignedEnvelopeWithTLSBinding creates a signed envelope of the desired
// type, with marshaled dataMsg and signs it. It also includes a TLS cert hash
// into the channel header
func CreateSignedEnvelopeWithTLSBinding(
txType common.HeaderType,
channelID string,
signer Signer,
dataMsg proto.Message,
msgVersion int32,
epoch uint64,
tlsCertHash []byte,
) (*common.Envelope, error) {
payloadChannelHeader := MakeChannelHeader(txType, msgVersion, channelID, epoch)
payloadChannelHeader.TlsCertHash = tlsCertHash
var err error
payloadSignatureHeader := &common.SignatureHeader{}
if signer != nil {
payloadSignatureHeader, err = NewSignatureHeader(signer)
if err != nil {
return nil, err
}
}
if !dataMsg.ProtoReflect().IsValid() {
return nil, errors.New("error marshaling: proto: Marshal called with nil")
}
data, err := proto.Marshal(dataMsg)
if err != nil {
return nil, errors.Wrap(err, "error marshaling")
}
paylBytes := MarshalOrPanic(
&common.Payload{
Header: MakePayloadHeader(payloadChannelHeader, payloadSignatureHeader),
Data: data,
},
)
var sig []byte
if signer != nil {
sig, err = signer.Sign(paylBytes)
if err != nil {
return nil, err
}
}
env := &common.Envelope{
Payload: paylBytes,
Signature: sig,
}
return env, nil
}
// Signer is the interface needed to sign a transaction
type Signer interface {
Sign(msg []byte) ([]byte, error)
Serialize() ([]byte, error)
}
// CreateSignedTx assembles an Envelope message from proposal, endorsements,
// and a signer. This function should be called by a client when it has
// collected enough endorsements for a proposal to create a transaction and
// submit it to peers for ordering
func CreateSignedTx(
proposal *peer.Proposal,
signer Signer,
resps ...*peer.ProposalResponse,
) (*common.Envelope, error) {
if len(resps) == 0 {
return nil, errors.New("at least one proposal response is required")
}
if signer == nil {
return nil, errors.New("signer is required when creating a signed transaction")
}
// the original header
hdr, err := UnmarshalHeader(proposal.Header)
if err != nil {
return nil, err
}
// the original payload
pPayl, err := UnmarshalChaincodeProposalPayload(proposal.Payload)
if err != nil {
return nil, err
}
// check that the signer is the same that is referenced in the header
signerBytes, err := signer.Serialize()
if err != nil {
return nil, err
}
shdr, err := UnmarshalSignatureHeader(hdr.SignatureHeader)
if err != nil {
return nil, err
}
if !bytes.Equal(signerBytes, shdr.Creator) {
return nil, errors.New("signer must be the same as the one referenced in the header")
}
// ensure that all actions are bitwise equal and that they are successful
var a1 []byte
for n, r := range resps {
if r.Response.Status < 200 || r.Response.Status >= 400 {
return nil, errors.Errorf("proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
}
if n == 0 {
a1 = r.Payload
continue
}
if !bytes.Equal(a1, r.Payload) {
return nil, errors.Errorf("ProposalResponsePayloads do not match (base64): '%s' vs '%s'",
b64.StdEncoding.EncodeToString(r.Payload), b64.StdEncoding.EncodeToString(a1))
}
}
// fill endorsements according to their uniqueness
endorsersUsed := make(map[string]struct{})
var endorsements []*peer.Endorsement
for _, r := range resps {
if r.Endorsement == nil {
continue
}
key := string(r.Endorsement.Endorser)
if _, used := endorsersUsed[key]; used {
continue
}
endorsements = append(endorsements, r.Endorsement)
endorsersUsed[key] = struct{}{}
}
if len(endorsements) == 0 {
return nil, errors.Errorf("no endorsements")
}
// create ChaincodeEndorsedAction
cea := &peer.ChaincodeEndorsedAction{ProposalResponsePayload: resps[0].Payload, Endorsements: endorsements}
// obtain the bytes of the proposal payload that will go to the transaction
propPayloadBytes, err := GetBytesProposalPayloadForTx(pPayl)
if err != nil {
return nil, err
}
// serialize the chaincode action payload
cap := &peer.ChaincodeActionPayload{ChaincodeProposalPayload: propPayloadBytes, Action: cea}
capBytes, err := GetBytesChaincodeActionPayload(cap)
if err != nil {
return nil, err
}
// create a transaction
taa := &peer.TransactionAction{Header: hdr.SignatureHeader, Payload: capBytes}
taas := make([]*peer.TransactionAction, 1)
taas[0] = taa
tx := &peer.Transaction{Actions: taas}
// serialize the tx
txBytes, err := GetBytesTransaction(tx)
if err != nil {
return nil, err
}
// create the payload
payl := &common.Payload{Header: hdr, Data: txBytes}
paylBytes, err := GetBytesPayload(payl)
if err != nil {
return nil, err
}
// sign the payload
sig, err := signer.Sign(paylBytes)
if err != nil {
return nil, err
}
// here's the envelope
return &common.Envelope{Payload: paylBytes, Signature: sig}, nil
}
// CreateProposalResponse creates a proposal response.
func CreateProposalResponse(
hdrbytes []byte,
payl []byte,
response *peer.Response,
results []byte,
events []byte,
ccid *peer.ChaincodeID,
signingEndorser Signer,
) (*peer.ProposalResponse, error) {
hdr, err := UnmarshalHeader(hdrbytes)
if err != nil {
return nil, err
}
// obtain the proposal hash given proposal header, payload and the
// requested visibility
pHashBytes, err := GetProposalHash1(hdr, payl)
if err != nil {
return nil, errors.WithMessage(err, "error computing proposal hash")
}
// get the bytes of the proposal response payload - we need to sign them
prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid)
if err != nil {
return nil, err
}
// serialize the signing identity
endorser, err := signingEndorser.Serialize()
if err != nil {
return nil, errors.WithMessage(err, "error serializing signing identity")
}
// sign the concatenation of the proposal response and the serialized
// endorser identity with this endorser's key
signature, err := signingEndorser.Sign(append(prpBytes, endorser...))
if err != nil {
return nil, errors.WithMessage(err, "could not sign the proposal response payload")
}
resp := &peer.ProposalResponse{
// Timestamp: TODO!
Version: 1, // TODO: pick right version number
Endorsement: &peer.Endorsement{
Signature: signature,
Endorser: endorser,
},
Payload: prpBytes,
Response: &peer.Response{
Status: 200,
Message: "OK",
},
}
return resp, nil
}
// CreateProposalResponseFailure creates a proposal response for cases where
// endorsement proposal fails either due to a endorsement failure or a
// chaincode failure (chaincode response status >= shim.ERRORTHRESHOLD)
func CreateProposalResponseFailure(
hdrbytes []byte,
payl []byte,
response *peer.Response,
results []byte,
events []byte,
chaincodeName string,
) (*peer.ProposalResponse, error) {
hdr, err := UnmarshalHeader(hdrbytes)
if err != nil {
return nil, err
}
// obtain the proposal hash given proposal header, payload and the requested visibility
pHashBytes, err := GetProposalHash1(hdr, payl)
if err != nil {
return nil, errors.WithMessage(err, "error computing proposal hash")
}
// get the bytes of the proposal response payload
prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, &peer.ChaincodeID{Name: chaincodeName})
if err != nil {
return nil, err
}
resp := &peer.ProposalResponse{
// Timestamp: TODO!
Payload: prpBytes,
Response: response,
}
return resp, nil
}
// GetSignedProposal returns a signed proposal given a Proposal message and a
// signing identity
func GetSignedProposal(prop *peer.Proposal, signer Signer) (*peer.SignedProposal, error) {
// check for nil argument
if prop == nil || signer == nil {
return nil, errors.New("nil arguments")
}
propBytes, err := proto.Marshal(prop)
if err != nil {
return nil, err
}
signature, err := signer.Sign(propBytes)
if err != nil {
return nil, err
}
return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, nil
}
// MockSignedEndorserProposalOrPanic creates a SignedProposal with the
// passed arguments
func MockSignedEndorserProposalOrPanic(
channelID string,
cs *peer.ChaincodeSpec,
creator,
signature []byte,
) (*peer.SignedProposal, *peer.Proposal) {
prop, _, err := CreateChaincodeProposal(
common.HeaderType_ENDORSER_TRANSACTION,
channelID,
&peer.ChaincodeInvocationSpec{ChaincodeSpec: cs},
creator)
if err != nil {
panic(err)
}
if prop == nil {
panic(errors.New("proto: Marshal called with nil"))
}
propBytes, err := proto.Marshal(prop)
if err != nil {
panic(err)
}
return &peer.SignedProposal{ProposalBytes: propBytes, Signature: signature}, prop
}
func MockSignedEndorserProposal2OrPanic(
channelID string,
cs *peer.ChaincodeSpec,
signer Signer,
) (*peer.SignedProposal, *peer.Proposal) {
serializedSigner, err := signer.Serialize()
if err != nil {
panic(err)
}
prop, _, err := CreateChaincodeProposal(
common.HeaderType_ENDORSER_TRANSACTION,
channelID,
&peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{}},
serializedSigner)
if err != nil {
panic(err)
}
sProp, err := GetSignedProposal(prop, signer)
if err != nil {
panic(err)
}
return sProp, prop
}
// GetBytesProposalPayloadForTx takes a ChaincodeProposalPayload and returns
// its serialized version according to the visibility field
func GetBytesProposalPayloadForTx(
payload *peer.ChaincodeProposalPayload,
) ([]byte, error) {
// check for nil argument
if payload == nil {
return nil, errors.New("nil arguments")
}
// strip the transient bytes off the payload
cppNoTransient := &peer.ChaincodeProposalPayload{Input: payload.Input, TransientMap: nil}
cppBytes, err := GetBytesChaincodeProposalPayload(cppNoTransient)
if err != nil {
return nil, err
}
return cppBytes, nil
}
// GetProposalHash2 gets the proposal hash - this version
// is called by the committer where the visibility policy
// has already been enforced and so we already get what
// we have to get in ccPropPayl
func GetProposalHash2(header *common.Header, ccPropPayl []byte) ([]byte, error) {
// check for nil argument
if header == nil ||
header.ChannelHeader == nil ||
header.SignatureHeader == nil ||
ccPropPayl == nil {
return nil, errors.New("nil arguments")
}
hash := sha256.New()
// hash the serialized Channel Header object
hash.Write(header.ChannelHeader)
// hash the serialized Signature Header object
hash.Write(header.SignatureHeader)
// hash the bytes of the chaincode proposal payload that we are given
hash.Write(ccPropPayl)
return hash.Sum(nil), nil
}
// GetProposalHash1 gets the proposal hash bytes after sanitizing the
// chaincode proposal payload according to the rules of visibility
func GetProposalHash1(header *common.Header, ccPropPayl []byte) ([]byte, error) {
// check for nil argument
if header == nil ||
header.ChannelHeader == nil ||
header.SignatureHeader == nil ||
ccPropPayl == nil {
return nil, errors.New("nil arguments")
}
// unmarshal the chaincode proposal payload
cpp, err := UnmarshalChaincodeProposalPayload(ccPropPayl)
if err != nil {
return nil, err
}
ppBytes, err := GetBytesProposalPayloadForTx(cpp)
if err != nil {
return nil, err
}
hash2 := sha256.New()
// hash the serialized Channel Header object
hash2.Write(header.ChannelHeader)
// hash the serialized Signature Header object
hash2.Write(header.SignatureHeader)
// hash of the part of the chaincode proposal payload that will go to the tx
hash2.Write(ppBytes)
return hash2.Sum(nil), nil
}
// GetOrComputeTxIDFromEnvelope gets the txID present in a given transaction
// envelope. If the txID is empty, it constructs the txID from nonce and
// creator fields in the envelope.
func GetOrComputeTxIDFromEnvelope(txEnvelopBytes []byte) (string, error) {
txEnvelope, err := UnmarshalEnvelope(txEnvelopBytes)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from envelope")
}
txPayload, err := UnmarshalPayload(txEnvelope.Payload)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from payload")
}
if txPayload.Header == nil {
return "", errors.New("error getting txID from header: payload header is nil")
}
chdr, err := UnmarshalChannelHeader(txPayload.Header.ChannelHeader)
if err != nil {
return "", errors.WithMessage(err, "error getting txID from channel header")
}
if chdr.TxId != "" {
return chdr.TxId, nil
}
sighdr, err := UnmarshalSignatureHeader(txPayload.Header.SignatureHeader)
if err != nil {
return "", errors.WithMessage(err, "error getting nonce and creator for computing txID")
}
txid := ComputeTxID(sighdr.Nonce, sighdr.Creator)
return txid, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package protoutil
import (
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset"
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset"
"github.com/hyperledger/fabric-protos-go-apiv2/msp"
"github.com/hyperledger/fabric-protos-go-apiv2/peer"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// the implicit contract of all these unmarshalers is that they
// will return a non-nil pointer whenever the error is nil
// UnmarshalBlock unmarshals bytes to a Block
func UnmarshalBlock(encoded []byte) (*common.Block, error) {
block := &common.Block{}
err := proto.Unmarshal(encoded, block)
return block, errors.Wrap(err, "error unmarshalling Block")
}
// UnmarshalChaincodeDeploymentSpec unmarshals bytes to a ChaincodeDeploymentSpec
func UnmarshalChaincodeDeploymentSpec(code []byte) (*peer.ChaincodeDeploymentSpec, error) {
cds := &peer.ChaincodeDeploymentSpec{}
err := proto.Unmarshal(code, cds)
return cds, errors.Wrap(err, "error unmarshalling ChaincodeDeploymentSpec")
}
// UnmarshalChaincodeInvocationSpec unmarshals bytes to a ChaincodeInvocationSpec
func UnmarshalChaincodeInvocationSpec(encoded []byte) (*peer.ChaincodeInvocationSpec, error) {
cis := &peer.ChaincodeInvocationSpec{}
err := proto.Unmarshal(encoded, cis)
return cis, errors.Wrap(err, "error unmarshalling ChaincodeInvocationSpec")
}
// UnmarshalPayload unmarshals bytes to a Payload
func UnmarshalPayload(encoded []byte) (*common.Payload, error) {
payload := &common.Payload{}
err := proto.Unmarshal(encoded, payload)
return payload, errors.Wrap(err, "error unmarshalling Payload")
}
// UnmarshalEnvelope unmarshals bytes to a Envelope
func UnmarshalEnvelope(encoded []byte) (*common.Envelope, error) {
envelope := &common.Envelope{}
err := proto.Unmarshal(encoded, envelope)
return envelope, errors.Wrap(err, "error unmarshalling Envelope")
}
// UnmarshalChannelHeader unmarshals bytes to a ChannelHeader
func UnmarshalChannelHeader(bytes []byte) (*common.ChannelHeader, error) {
chdr := &common.ChannelHeader{}
err := proto.Unmarshal(bytes, chdr)
return chdr, errors.Wrap(err, "error unmarshalling ChannelHeader")
}
// UnmarshalChaincodeID unmarshals bytes to a ChaincodeID
func UnmarshalChaincodeID(bytes []byte) (*peer.ChaincodeID, error) {
ccid := &peer.ChaincodeID{}
err := proto.Unmarshal(bytes, ccid)
return ccid, errors.Wrap(err, "error unmarshalling ChaincodeID")
}
// UnmarshalSignatureHeader unmarshals bytes to a SignatureHeader
func UnmarshalSignatureHeader(bytes []byte) (*common.SignatureHeader, error) {
sh := &common.SignatureHeader{}
err := proto.Unmarshal(bytes, sh)
return sh, errors.Wrap(err, "error unmarshalling SignatureHeader")
}
// UnmarshalIdentifierHeader unmarshals bytes to an IdentifierHeader
func UnmarshalIdentifierHeader(bytes []byte) (*common.IdentifierHeader, error) {
ih := &common.IdentifierHeader{}
err := proto.Unmarshal(bytes, ih)
return ih, errors.Wrap(err, "error unmarshalling IdentifierHeader")
}
func UnmarshalSerializedIdentity(bytes []byte) (*msp.SerializedIdentity, error) {
sid := &msp.SerializedIdentity{}
err := proto.Unmarshal(bytes, sid)
return sid, errors.Wrap(err, "error unmarshalling SerializedIdentity")
}
// UnmarshalHeader unmarshals bytes to a Header
func UnmarshalHeader(bytes []byte) (*common.Header, error) {
hdr := &common.Header{}
err := proto.Unmarshal(bytes, hdr)
return hdr, errors.Wrap(err, "error unmarshalling Header")
}
// UnmarshalConfigEnvelope unmarshals bytes to a ConfigEnvelope
func UnmarshalConfigEnvelope(bytes []byte) (*common.ConfigEnvelope, error) {
cfg := &common.ConfigEnvelope{}
err := proto.Unmarshal(bytes, cfg)
return cfg, errors.Wrap(err, "error unmarshalling ConfigEnvelope")
}
// UnmarshalChaincodeHeaderExtension unmarshals bytes to a ChaincodeHeaderExtension
func UnmarshalChaincodeHeaderExtension(hdrExtension []byte) (*peer.ChaincodeHeaderExtension, error) {
chaincodeHdrExt := &peer.ChaincodeHeaderExtension{}
err := proto.Unmarshal(hdrExtension, chaincodeHdrExt)
return chaincodeHdrExt, errors.Wrap(err, "error unmarshalling ChaincodeHeaderExtension")
}
// UnmarshalProposalResponse unmarshals bytes to a ProposalResponse
func UnmarshalProposalResponse(prBytes []byte) (*peer.ProposalResponse, error) {
proposalResponse := &peer.ProposalResponse{}
err := proto.Unmarshal(prBytes, proposalResponse)
return proposalResponse, errors.Wrap(err, "error unmarshalling ProposalResponse")
}
// UnmarshalChaincodeAction unmarshals bytes to a ChaincodeAction
func UnmarshalChaincodeAction(caBytes []byte) (*peer.ChaincodeAction, error) {
chaincodeAction := &peer.ChaincodeAction{}
err := proto.Unmarshal(caBytes, chaincodeAction)
return chaincodeAction, errors.Wrap(err, "error unmarshalling ChaincodeAction")
}
// UnmarshalResponse unmarshals bytes to a Response
func UnmarshalResponse(resBytes []byte) (*peer.Response, error) {
response := &peer.Response{}
err := proto.Unmarshal(resBytes, response)
return response, errors.Wrap(err, "error unmarshalling Response")
}
// UnmarshalChaincodeEvents unmarshals bytes to a ChaincodeEvent
func UnmarshalChaincodeEvents(eBytes []byte) (*peer.ChaincodeEvent, error) {
chaincodeEvent := &peer.ChaincodeEvent{}
err := proto.Unmarshal(eBytes, chaincodeEvent)
return chaincodeEvent, errors.Wrap(err, "error unmarshalling ChaicnodeEvent")
}
// UnmarshalProposalResponsePayload unmarshals bytes to a ProposalResponsePayload
func UnmarshalProposalResponsePayload(prpBytes []byte) (*peer.ProposalResponsePayload, error) {
prp := &peer.ProposalResponsePayload{}
err := proto.Unmarshal(prpBytes, prp)
return prp, errors.Wrap(err, "error unmarshalling ProposalResponsePayload")
}
// UnmarshalProposal unmarshals bytes to a Proposal
func UnmarshalProposal(propBytes []byte) (*peer.Proposal, error) {
prop := &peer.Proposal{}
err := proto.Unmarshal(propBytes, prop)
return prop, errors.Wrap(err, "error unmarshalling Proposal")
}
// UnmarshalTransaction unmarshals bytes to a Transaction
func UnmarshalTransaction(txBytes []byte) (*peer.Transaction, error) {
tx := &peer.Transaction{}
err := proto.Unmarshal(txBytes, tx)
return tx, errors.Wrap(err, "error unmarshalling Transaction")
}
// UnmarshalChaincodeActionPayload unmarshals bytes to a ChaincodeActionPayload
func UnmarshalChaincodeActionPayload(capBytes []byte) (*peer.ChaincodeActionPayload, error) {
cap := &peer.ChaincodeActionPayload{}
err := proto.Unmarshal(capBytes, cap)
return cap, errors.Wrap(err, "error unmarshalling ChaincodeActionPayload")
}
// UnmarshalChaincodeProposalPayload unmarshals bytes to a ChaincodeProposalPayload
func UnmarshalChaincodeProposalPayload(bytes []byte) (*peer.ChaincodeProposalPayload, error) {
cpp := &peer.ChaincodeProposalPayload{}
err := proto.Unmarshal(bytes, cpp)
return cpp, errors.Wrap(err, "error unmarshalling ChaincodeProposalPayload")
}
// UnmarshalTxReadWriteSet unmarshals bytes to a TxReadWriteSet
func UnmarshalTxReadWriteSet(bytes []byte) (*rwset.TxReadWriteSet, error) {
rws := &rwset.TxReadWriteSet{}
err := proto.Unmarshal(bytes, rws)
return rws, errors.Wrap(err, "error unmarshalling TxReadWriteSet")
}
// UnmarshalKVRWSet unmarshals bytes to a KVRWSet
func UnmarshalKVRWSet(bytes []byte) (*kvrwset.KVRWSet, error) {
rws := &kvrwset.KVRWSet{}
err := proto.Unmarshal(bytes, rws)
return rws, errors.Wrap(err, "error unmarshalling KVRWSet")
}
// UnmarshalHashedRWSet unmarshals bytes to a HashedRWSet
func UnmarshalHashedRWSet(bytes []byte) (*kvrwset.HashedRWSet, error) {
hrws := &kvrwset.HashedRWSet{}
err := proto.Unmarshal(bytes, hrws)
return hrws, errors.Wrap(err, "error unmarshalling HashedRWSet")
}
// UnmarshalSignaturePolicy unmarshals bytes to a SignaturePolicyEnvelope
func UnmarshalSignaturePolicy(bytes []byte) (*common.SignaturePolicyEnvelope, error) {
sp := &common.SignaturePolicyEnvelope{}
err := proto.Unmarshal(bytes, sp)
return sp, errors.Wrap(err, "error unmarshalling SignaturePolicyEnvelope")
}
// UnmarshalPayloadOrPanic unmarshals bytes to a Payload structure or panics
// on error
func UnmarshalPayloadOrPanic(encoded []byte) *common.Payload {
payload, err := UnmarshalPayload(encoded)
if err != nil {
panic(err)
}
return payload
}
// UnmarshalEnvelopeOrPanic unmarshals bytes to an Envelope structure or panics
// on error
func UnmarshalEnvelopeOrPanic(encoded []byte) *common.Envelope {
envelope, err := UnmarshalEnvelope(encoded)
if err != nil {
panic(err)
}
return envelope
}
// UnmarshalBlockOrPanic unmarshals bytes to an Block or panics
// on error
func UnmarshalBlockOrPanic(encoded []byte) *common.Block {
block, err := UnmarshalBlock(encoded)
if err != nil {
panic(err)
}
return block
}
// UnmarshalChannelHeaderOrPanic unmarshals bytes to a ChannelHeader or panics
// on error
func UnmarshalChannelHeaderOrPanic(bytes []byte) *common.ChannelHeader {
chdr, err := UnmarshalChannelHeader(bytes)
if err != nil {
panic(err)
}
return chdr
}
// UnmarshalSignatureHeaderOrPanic unmarshals bytes to a SignatureHeader or panics
// on error
func UnmarshalSignatureHeaderOrPanic(bytes []byte) *common.SignatureHeader {
sighdr, err := UnmarshalSignatureHeader(bytes)
if err != nil {
panic(err)
}
return sighdr
}