Skip to content

Commit

Permalink
[FAB-187] - using policies in VSCC
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ale-linux committed Jan 17, 2017
1 parent ae10d2b commit 0377199
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 40 deletions.
12 changes: 6 additions & 6 deletions common/cauthdsl/cauthdsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial

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

if verified >= t.From.N {
cauthdslLogger.Debug("Gate evaluation succeeds: (%s)", t)
cauthdslLogger.Debugf("Gate evaluation succeeds: (%s)", t)
} else {
cauthdslLogger.Debug("Gate evaluation fails: (%s)", t)
cauthdslLogger.Debugf("Gate evaluation fails: (%s)", t)
}

return verified >= t.From.N
Expand All @@ -68,7 +68,7 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
}
signedByID := identities[t.SignedBy]
return func(signedData []*cb.SignedData, used []bool) bool {
cauthdslLogger.Debug("Principal evaluation starts: (%s) (used %s)", t, used)
cauthdslLogger.Debugf("Principal evaluation starts: (%s) (used %s)", t, used)
for i, sd := range signedData {
if used[i] {
continue
Expand All @@ -79,13 +79,13 @@ func compile(policy *cb.SignaturePolicy, identities []*cb.MSPPrincipal, deserial
if err == nil {
err := identity.Verify(sd.Data, sd.Signature)
if err == nil {
cauthdslLogger.Debug("Principal evaluation succeeds: (%s)", t, used)
cauthdslLogger.Debugf("Principal evaluation succeeds: (%s)", t, used)
used[i] = true
return true
}
}
}
cauthdslLogger.Debug("Principal evaluation fails: (%s)", t, used)
cauthdslLogger.Debugf("Principal evaluation fails: (%s)", t, used)
return false
}, nil
default:
Expand Down
19 changes: 19 additions & 0 deletions common/cauthdsl/cauthdsl_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
cb "github.com/hyperledger/fabric/protos/common"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/protos/utils"
)

// AcceptAllPolicy always evaluates to true
Expand Down Expand Up @@ -73,6 +74,24 @@ func SignedBy(index int32) *cb.SignaturePolicy {
}
}

// SignedByMspMember creates a SignaturePolicyEnvelope
// requiring 1 signature from any member of the specified MSP
func SignedByMspMember(mspId string) *cb.SignaturePolicyEnvelope {
// specify the principal: it's a member of the msp we just found
principal := &cb.MSPPrincipal{
PrincipalClassification: cb.MSPPrincipal_ByMSPRole,
Principal: utils.MarshalOrPanic(&cb.MSPRole{Role: cb.MSPRole_Member, MSPIdentifier: mspId})}

// create the policy: it requires exactly 1 signature from the first (and only) principal
p := &cb.SignaturePolicyEnvelope{
Version: 0,
Policy: NOutOf(1, []*cb.SignaturePolicy{SignedBy(0)}),
Identities: []*cb.MSPPrincipal{principal},
}

return p
}

// 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})
Expand Down
8 changes: 4 additions & 4 deletions core/chaincode/ccproviderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc boo
return &ccProviderContextImpl{ctx: ctx}
}

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

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

return vscc, nil
return vscc, data.Policy, nil
}

// ExecuteChaincode executes the chaincode specified in the context with the specified arguments
Expand Down
55 changes: 53 additions & 2 deletions core/committer/txvalidator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ import (
"fmt"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/cauthdsl"
coreUtil "github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/validation"
"github.com/hyperledger/fabric/core/ledger"
ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
"github.com/hyperledger/fabric/core/peer/msp"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protos/common"
"github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"
"github.com/syndtr/goleveldb/leveldb/errors"
)

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

// getHardcodedPolicy returns a policy that requests
// one valid signature from the first MSP in this
// chain's MSP manager
// FIXME: this needs to be removed as soon as we extract the policy from LCCC
func getHardcodedPolicy(chainID string) ([]byte, error) {
// 1) determine the MSP identifier for the first MSP in this chain
var msp msp.MSP
mspMgr := mspmgmt.GetManagerForChain(chainID)
msps, err := mspMgr.GetMSPs()
if err != nil {
return nil, fmt.Errorf("Could not retrieve the MSPs for the chain manager, err %s", err)
}
if len(msps) == 0 {
return nil, errors.New("At least one MSP was expected")
}
for _, m := range msps {
msp = m
break
}
mspid, err := msp.GetIdentifier()
if err != nil {
return nil, fmt.Errorf("Failure getting the msp identifier, err %s", err)
}

// 2) get the policy
p := cauthdsl.SignedByMspMember(mspid)

// 3) marshal it and return it
b, err := proto.Marshal(p)
if err != nil {
return nil, fmt.Errorf("Could not marshal policy, err %s", err)
}

return b, err
}

func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error {
// Chain ID
chainID := payload.Header.ChainHeader.ChainID
Expand All @@ -171,10 +211,20 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
return err
}

// TODO: temporary workaround until the policy is specified
// by the deployer and can be retrieved via LCCC: we create
// a policy that requests 1 valid signature from this chain's
// MSP
policy, err := getHardcodedPolicy(chainID)
if err != nil {
return err
}

// build arguments for VSCC invocation
// args[0] - function name (not used now)
// args[1] - serialized Envelope
args := [][]byte{[]byte(""), envBytes}
// args[2] - serialized policy
args := [][]byte{[]byte(""), envBytes, policy}

ctxt, err := v.ccprovider.GetContext(v.ledger)
if err != nil {
Expand All @@ -196,7 +246,8 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b
vscc := "vscc"
if hdrExt.ChaincodeID.Name != "lccc" {
// Extracting vscc from lccc
vscc, err = v.ccprovider.GetVSCCFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
// TODO: extract policy as well when available; it's the second argument returned by GetCCValidationInfoFromLCCC
vscc, _, err = v.ccprovider.GetCCValidationInfoFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name)
if err != nil {
logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err)
return err
Expand Down
4 changes: 2 additions & 2 deletions core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type ChaincodeProvider interface {
GetContext(ledger ledger.ValidatedLedger) (context.Context, error)
// GetCCContext returns an opaque chaincode context
GetCCContext(cid, name, version, txid string, syscc bool, prop *peer.Proposal) interface{}
// GetVSCCFromLCCC returns the VSCC listed by LCCC for the supplied chaincode
GetVSCCFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, error)
// GetCCValidationInfoFromLCCC returns the VSCC and the policy listed by LCCC for the supplied chaincode
GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, prop *peer.Proposal, chainID string, chaincodeID string) (string, []byte, error)
// ExecuteChaincode executes the chaincode given context and args
ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) ([]byte, *peer.ChaincodeEvent, error)
// ReleaseContext releases the context returned previously by GetContext
Expand Down
6 changes: 3 additions & 3 deletions core/mocks/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ func (c *mockCcProviderImpl) GetCCContext(cid, name, version, txid string, syscc
return &mockCcProviderContextImpl{}
}

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

// ExecuteChaincode does nothing
Expand Down
60 changes: 38 additions & 22 deletions core/system_chaincode/vscc/validator_onevalidsignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"

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

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

if args[2] == nil {
return nil, errors.New("No policy supplied")
}

logger.Infof("VSCC invoked")

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

// get the policy
mgr := mspmgmt.GetManagerForChain(payl.Header.ChainHeader.ChainID)
pProvider := cauthdsl.NewPolicyProvider(mgr)
policy, err := pProvider.NewPolicy(args[2])
if err != nil {
logger.Errorf("VSCC error: pProvider.NewPolicy failed, err %s", err)
return nil, err
}

// validate the payload type
if common.HeaderType(payl.Header.ChainHeader.Type) != common.HeaderType_ENDORSER_TRANSACTION {
logger.Errorf("Only Endorser Transactions are supported, provided type %d", payl.Header.ChainHeader.Type)
Expand All @@ -99,29 +115,29 @@ func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface)
return nil, err
}

// this is what is being signed
// this is the first part of the signed message
prespBytes := cap.Action.ProposalResponsePayload

// loop through each of the endorsements
for _, endorsement := range cap.Action.Endorsements {
// extract the identity of the signer
end, err := mspmgmt.GetManagerForChain(payl.Header.ChainHeader.ChainID).DeserializeIdentity(endorsement.Endorser)
if err != nil {
logger.Errorf("VSCC error: DeserializeIdentity failed, err %s", err)
return nil, err
}

// validate it
err = end.Validate()
if err != nil {
return nil, fmt.Errorf("Invalid endorser identity, err %s", err)
// build the signature set for the evaluation
signatureSet := make([]*common.SignedData, len(cap.Action.Endorsements))

// loop through each of the endorsements and build the signature set
for i, endorsement := range cap.Action.Endorsements {
signatureSet[i] = &common.SignedData{
// set the data that is signed; concatenation of proposal response bytes and endorser ID
Data: append(prespBytes, endorsement.Endorser...),
// set the identity that signs the message: it's the endorser
Identity: endorsement.Endorser,
// set the signature
Signature: endorsement.Signature,
}
}

// verify the signature
err = end.Verify(append(prespBytes, endorsement.Endorser...), endorsement.Signature)
if err != nil {
return nil, fmt.Errorf("Invalid signature, err %s", err)
}
// evaluate the signature set against the policy
err = policy.Evaluate(signatureSet)
if err != nil {
logger.Errorf("VSCC error: policy evaluation failed, err %s", err)
return nil, err
}
}

Expand Down
Loading

0 comments on commit 0377199

Please sign in to comment.