Skip to content

Commit 0377199

Browse files
committed
[FAB-187] - using policies in VSCC
This code drop integrates cauthdsl into the VSCC. The flow is the following: the validator code extracts from LCCC the policy specified by the deployer. The policy is an additional argument to VSCC. VSCC deserializes the policy and uses cauthdsl to validate the endorsements against it. Currently the chaincode deployer doesn't specify a policy when it deploys a chaincode and so for now the validator uses a policy that says "signed by any member of the specified MSP". This is to be removed as soon as chaincode deploy transactions specify policies when a new chaincode is deployed. Change-Id: I9e5ff1effa75934cdfef231a37e66f2941a45d10 Signed-off-by: Alessandro Sorniotti <[email protected]>
1 parent ae10d2b commit 0377199

File tree

8 files changed

+182
-40
lines changed

8 files changed

+182
-40
lines changed

common/cauthdsl/cauthdsl.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
4343

4444
}
4545
return func(signedData []*cb.SignedData, used []bool) bool {
46-
cauthdslLogger.Debug("Gate evaluation starts: (%s)", t)
46+
cauthdslLogger.Debugf("Gate evaluation starts: (%s)", t)
4747
verified := int32(0)
4848
_used := make([]bool, len(used))
4949
for _, policy := range policies {
@@ -55,9 +55,9 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
5555
}
5656

5757
if verified >= t.From.N {
58-
cauthdslLogger.Debug("Gate evaluation succeeds: (%s)", t)
58+
cauthdslLogger.Debugf("Gate evaluation succeeds: (%s)", t)
5959
} else {
60-
cauthdslLogger.Debug("Gate evaluation fails: (%s)", t)
60+
cauthdslLogger.Debugf("Gate evaluation fails: (%s)", t)
6161
}
6262

6363
return verified >= t.From.N
@@ -68,7 +68,7 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
6868
}
6969
signedByID := identities[t.SignedBy]
7070
return func(signedData []*cb.SignedData, used []bool) bool {
71-
cauthdslLogger.Debug("Principal evaluation starts: (%s) (used %s)", t, used)
71+
cauthdslLogger.Debugf("Principal evaluation starts: (%s) (used %s)", t, used)
7272
for i, sd := range signedData {
7373
if used[i] {
7474
continue
@@ -79,13 +79,13 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
7979
if err == nil {
8080
err := identity.Verify(sd.Data, sd.Signature)
8181
if err == nil {
82-
cauthdslLogger.Debug("Principal evaluation succeeds: (%s)", t, used)
82+
cauthdslLogger.Debugf("Principal evaluation succeeds: (%s)", t, used)
8383
used[i] = true
8484
return true
8585
}
8686
}
8787
}
88-
cauthdslLogger.Debug("Principal evaluation fails: (%s)", t, used)
88+
cauthdslLogger.Debugf("Principal evaluation fails: (%s)", t, used)
8989
return false
9090
}, nil
9191
default:

common/cauthdsl/cauthdsl_builder.go

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
cb "github.com/hyperledger/fabric/protos/common"
2121

2222
"github.com/golang/protobuf/proto"
23+
"github.com/hyperledger/fabric/protos/utils"
2324
)
2425

2526
// AcceptAllPolicy always evaluates to true
@@ -73,6 +74,24 @@ func SignedBy(index int32) *cb.SignaturePolicy {
7374
}
7475
}
7576

77+
// SignedByMspMember creates a SignaturePolicyEnvelope
78+
// requiring 1 signature from any member of the specified MSP
79+
func SignedByMspMember(mspId string) *cb.SignaturePolicyEnvelope {
80+
// specify the principal: it's a member of the msp we just found
81+
principal := &cb.MSPPrincipal{
82+
PrincipalClassification: cb.MSPPrincipal_ByMSPRole,
83+
Principal: utils.MarshalOrPanic(&cb.MSPRole{Role: cb.MSPRole_Member, MSPIdentifier: mspId})}
84+
85+
// create the policy: it requires exactly 1 signature from the first (and only) principal
86+
p := &cb.SignaturePolicyEnvelope{
87+
Version: 0,
88+
Policy: NOutOf(1, []*cb.SignaturePolicy{SignedBy(0)}),
89+
Identities: []*cb.MSPPrincipal{principal},
90+
}
91+
92+
return p
93+
}
94+
7695
// And is a convenience method which utilizes NOutOf to produce And equivalent behavior
7796
func And(lhs, rhs *cb.SignaturePolicy) *cb.SignaturePolicy {
7897
return NOutOf(2, []*cb.SignaturePolicy{lhs, rhs})

core/chaincode/ccproviderimpl.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc boo
5454
return &ccProviderContextImpl{ctx: ctx}
5555
}
5656

57-
// GetVSCCFromLCCC returns the VSCC listed in LCCC for the supplied chaincode
58-
func (c *ccProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error) {
57+
// GetCCValidationInfoFromLCCC returns the VSCC and the policy listed in LCCC for the supplied chaincode
58+
func (c *ccProviderImpl) GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, []byte, error) {
5959
data, err := GetChaincodeDataFromLCCC(ctxt, txid, prop, chainID, chaincodeID)
6060
if err != nil {
61-
return "", err
61+
return "", nil, err
6262
}
6363

6464
vscc := "vscc"
@@ -67,7 +67,7 @@ func (c *ccProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop
6767
vscc = data.Vscc
6868
}
6969

70-
return vscc, nil
70+
return vscc, data.Policy, nil
7171
}
7272

7373
// ExecuteChaincode executes the chaincode specified in the context with the specified arguments

core/committer/txvalidator/validator.go

+53-2
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ import (
2020
"fmt"
2121

2222
"github.com/golang/protobuf/proto"
23+
"github.com/hyperledger/fabric/common/cauthdsl"
2324
coreUtil "github.com/hyperledger/fabric/common/util"
2425
"github.com/hyperledger/fabric/core/common/ccprovider"
2526
"github.com/hyperledger/fabric/core/common/validation"
2627
"github.com/hyperledger/fabric/core/ledger"
2728
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
29+
"github.com/hyperledger/fabric/core/peer/msp"
30+
"github.com/hyperledger/fabric/msp"
2831
"github.com/hyperledger/fabric/protos/common"
2932
"github.com/hyperledger/fabric/protos/utils"
3033
"github.com/op/go-logging"
34+
"github.com/syndtr/goleveldb/leveldb/errors"
3135
)
3236

3337
//Validator interface which defines API to validate block transactions
@@ -153,6 +157,42 @@ func (v *txValidator) Validate(block *common.Block) {
153157
block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsfltr.ToBytes()
154158
}
155159

160+
// getHardcodedPolicy returns a policy that requests
161+
// one valid signature from the first MSP in this
162+
// chain's MSP manager
163+
// FIXME: this needs to be removed as soon as we extract the policy from LCCC
164+
func getHardcodedPolicy(chainID string) ([]byte, error) {
165+
// 1) determine the MSP identifier for the first MSP in this chain
166+
var msp msp.MSP
167+
mspMgr := mspmgmt.GetManagerForChain(chainID)
168+
msps, err := mspMgr.GetMSPs()
169+
if err != nil {
170+
return nil, fmt.Errorf("Could not retrieve the MSPs for the chain manager, err %s", err)
171+
}
172+
if len(msps) == 0 {
173+
return nil, errors.New("At least one MSP was expected")
174+
}
175+
for _, m := range msps {
176+
msp = m
177+
break
178+
}
179+
mspid, err := msp.GetIdentifier()
180+
if err != nil {
181+
return nil, fmt.Errorf("Failure getting the msp identifier, err %s", err)
182+
}
183+
184+
// 2) get the policy
185+
p := cauthdsl.SignedByMspMember(mspid)
186+
187+
// 3) marshal it and return it
188+
b, err := proto.Marshal(p)
189+
if err != nil {
190+
return nil, fmt.Errorf("Could not marshal policy, err %s", err)
191+
}
192+
193+
return b, err
194+
}
195+
156196
func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
157197
// Chain ID
158198
chainID := payload.Header.ChainHeader.ChainID
@@ -171,10 +211,20 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
171211
return err
172212
}
173213

214+
// TODO: temporary workaround until the policy is specified
215+
// by the deployer and can be retrieved via LCCC: we create
216+
// a policy that requests 1 valid signature from this chain's
217+
// MSP
218+
policy, err := getHardcodedPolicy(chainID)
219+
if err != nil {
220+
return err
221+
}
222+
174223
// build arguments for VSCC invocation
175224
// args[0] - function name (not used now)
176225
// args[1] - serialized Envelope
177-
args := [][]byte{[]byte(""), envBytes}
226+
// args[2] - serialized policy
227+
args := [][]byte{[]byte(""), envBytes, policy}
178228

179229
ctxt, err := v.ccprovider.GetContext(v.ledger)
180230
if err != nil {
@@ -196,7 +246,8 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
196246
vscc := "vscc"
197247
if hdrExt.ChaincodeID.Name != "lccc" {
198248
// Extracting vscc from lccc
199-
vscc, err = v.ccprovider.GetVSCCFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
249+
// TODO: extract policy as well when available; it's the second argument returned by GetCCValidationInfoFromLCCC
250+
vscc, _, err = v.ccprovider.GetCCValidationInfoFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
200251
if err != nil {
201252
logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err)
202253
return err

core/common/ccprovider/ccprovider.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ type ChaincodeProvider interface {
1616
GetContext(ledger ledger.ValidatedLedger) (context.Context, error)
1717
// GetCCContext returns an opaque chaincode context
1818
GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{}
19-
// GetVSCCFromLCCC returns the VSCC listed by LCCC for the supplied chaincode
20-
GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error)
19+
// GetCCValidationInfoFromLCCC returns the VSCC and the policy listed by LCCC for the supplied chaincode
20+
GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, []byte, error)
2121
// ExecuteChaincode executes the chaincode given context and args
2222
ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error)
2323
// ReleaseContext releases the context returned previously by GetContext

core/mocks/ccprovider/ccprovider.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ func (c *mockCcProviderImpl) GetCCContext(cid, name, version, txid string, syscc
3535
return &mockCcProviderContextImpl{}
3636
}
3737

38-
// GetVSCCFromLCCC does nothing
39-
func (c *mockCcProviderImpl) GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error) {
40-
return "vscc", nil
38+
// GetCCValidationInfoFromLCCC does nothing
39+
func (c *mockCcProviderImpl) GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, []byte, error) {
40+
return "vscc", nil, nil
4141
}
4242

4343
// ExecuteChaincode does nothing

core/system_chaincode/vscc/validator_onevalidsignature.go

+38-22
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222

23+
"github.com/hyperledger/fabric/common/cauthdsl"
2324
"github.com/hyperledger/fabric/core/chaincode/shim"
2425
"github.com/hyperledger/fabric/core/peer/msp"
2526
"github.com/hyperledger/fabric/protos/common"
@@ -48,20 +49,26 @@ func (vscc *ValidatorOneValidSignature) Init(stub shim.ChaincodeStubInterface) (
4849
// policy specification to be coded as a transaction of the chaincode and the client
4950
// selecting which policy to use for validation using parameter function
5051
// @return serialized Block of valid and invalid transactions indentified
51-
// Note that Peer calls this function with 2 arguments, where args[0] is the
52-
// function name and args[1] is the Envelope
52+
// Note that Peer calls this function with 3 arguments, where args[0] is the
53+
// function name, args[1] is the Envelope and args[2] is the validation policy
5354
func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
55+
// TODO: document the argument in some white paper or design document
5456
// args[0] - function name (not used now)
5557
// args[1] - serialized Envelope
58+
// args[2] - serialized policy
5659
args := stub.GetArgs()
57-
if len(args) < 2 {
60+
if len(args) < 3 {
5861
return nil, errors.New("Incorrect number of arguments")
5962
}
6063

6164
if args[1] == nil {
6265
return nil, errors.New("No block to validate")
6366
}
6467

68+
if args[2] == nil {
69+
return nil, errors.New("No policy supplied")
70+
}
71+
6572
logger.Infof("VSCC invoked")
6673

6774
// get the envelope...
@@ -78,6 +85,15 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
7885
return nil, err
7986
}
8087

88+
// get the policy
89+
mgr := mspmgmt.GetManagerForChain(payl.Header.ChainHeader.ChainID)
90+
pProvider := cauthdsl.NewPolicyProvider(mgr)
91+
policy, err := pProvider.NewPolicy(args[2])
92+
if err != nil {
93+
logger.Errorf("VSCC error: pProvider.NewPolicy failed, err %s", err)
94+
return nil, err
95+
}
96+
8197
// validate the payload type
8298
if common.HeaderType(payl.Header.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION {
8399
logger.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type)
@@ -99,29 +115,29 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
99115
return nil, err
100116
}
101117

102-
// this is what is being signed
118+
// this is the first part of the signed message
103119
prespBytes := cap.Action.ProposalResponsePayload
104120

105-
// loop through each of the endorsements
106-
for _, endorsement := range cap.Action.Endorsements {
107-
// extract the identity of the signer
108-
end, err := mspmgmt.GetManagerForChain(payl.Header.ChainHeader.ChainID).DeserializeIdentity(endorsement.Endorser)
109-
if err != nil {
110-
logger.Errorf("VSCC error: DeserializeIdentity failed, err %s", err)
111-
return nil, err
112-
}
113-
114-
// validate it
115-
err = end.Validate()
116-
if err != nil {
117-
return nil, fmt.Errorf("Invalid endorser identity, err %s", err)
121+
// build the signature set for the evaluation
122+
signatureSet := make([]*common.SignedData, len(cap.Action.Endorsements))
123+
124+
// loop through each of the endorsements and build the signature set
125+
for i, endorsement := range cap.Action.Endorsements {
126+
signatureSet[i] = &common.SignedData{
127+
// set the data that is signed; concatenation of proposal response bytes and endorser ID
128+
Data: append(prespBytes, endorsement.Endorser...),
129+
// set the identity that signs the message: it's the endorser
130+
Identity: endorsement.Endorser,
131+
// set the signature
132+
Signature: endorsement.Signature,
118133
}
134+
}
119135

120-
// verify the signature
121-
err = end.Verify(append(prespBytes, endorsement.Endorser...), endorsement.Signature)
122-
if err != nil {
123-
return nil, fmt.Errorf("Invalid signature, err %s", err)
124-
}
136+
// evaluate the signature set against the policy
137+
err = policy.Evaluate(signatureSet)
138+
if err != nil {
139+
logger.Errorf("VSCC error: policy evaluation failed, err %s", err)
140+
return nil, err
125141
}
126142
}
127143

0 commit comments

Comments
 (0)