Skip to content

Commit

Permalink
[FAB-11802] State-based val. must at least check CCEP
Browse files Browse the repository at this point in the history
FAB-9473 addressed the following issue: if a transaction doesn't perform
writes in the namespace of a chaincode, that chaincode's EP must still be
checked for the chaincode that is being invoked directly (and not by way of
a cc2cc invocation). This behaviour must not change with the
state-based validator.  However, we cannot adopt the 1.2 behaviour of
always checking the chaincode EP as that would invalidate the premise of
state-based endorsement. The following approach is taken: the state-based
validator will always validate the chaincode endorsement policy in case the
transaction did not require checking any other endorsement policy (notably,
any state-based one).

Change-Id: Ib8c1a1cdbf001994b12db1b8762a96369ab20855
Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net>
Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com>
  • Loading branch information
ale-linux committed Aug 30, 2018
1 parent d6987ff commit 5388376
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 23 deletions.
40 changes: 18 additions & 22 deletions core/common/validation/statebased/validator_keylevel.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ import (
/**********************************************************************************************************/

type policyChecker struct {
someEPChecked bool
ccEPChecked bool
vpmgr KeyLevelValidationParameterManager
policySupport validation.PolicyEvaluator
ccEP []byte
signatureSet []*common.SignedData
}

func (p *policyChecker) checkCCEP(cc string, blockNum, txNum uint64) commonerrors.TxValidationError {
if p.ccEPChecked {
func (p *policyChecker) checkCCEPIfCondition(cc string, blockNum, txNum uint64, condition bool) commonerrors.TxValidationError {
if condition {
return nil
}

Expand All @@ -42,9 +43,18 @@ func (p *policyChecker) checkCCEP(cc string, blockNum, txNum uint64) commonerror
}

p.ccEPChecked = true
p.someEPChecked = true
return nil
}

func (p *policyChecker) checkCCEPIfNotChecked(cc string, blockNum, txNum uint64) commonerrors.TxValidationError {
return p.checkCCEPIfCondition(cc, blockNum, txNum, p.ccEPChecked)
}

func (p *policyChecker) checkCCEPIfNoEPChecked(cc string, blockNum, txNum uint64) commonerrors.TxValidationError {
return p.checkCCEPIfCondition(cc, blockNum, txNum, p.someEPChecked)
}

func (p *policyChecker) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uint64) commonerrors.TxValidationError {
// see if there is a key-level validation parameter for this key
vp, err := p.vpmgr.GetValidationParameterForKey(cc, coll, key, blockNum, txNum)
Expand All @@ -61,7 +71,7 @@ func (p *policyChecker) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uin

// if no key-level validation parameter has been specified, the regular cc endorsement policy needs to hold
if len(vp) == 0 {
return p.checkCCEP(cc, blockNum, txNum)
return p.checkCCEPIfNotChecked(cc, blockNum, txNum)
}

// validate against key-level vp
Expand All @@ -70,6 +80,8 @@ func (p *policyChecker) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uin
return policyErr(errors.Wrapf(err, "validation of key %s (coll'%s':ns'%s') in tx %d:%d failed", key, coll, cc, blockNum, txNum))
}

p.someEPChecked = true

return nil
}

Expand Down Expand Up @@ -244,26 +256,10 @@ func (klv *KeyLevelValidator) Validate(cc string, blockNum, txNum uint64, rwsetB
}
}
}
// public reads
// if there are any we check the chaincode endorsement policy because of FAB-9473
if len(nsRWSet.KvRwSet.Reads) > 0 {
err := policyChecker.checkCCEP(cc, blockNum, txNum)
if err != nil {
return err
}
}
// private reads
// if there are any we check the chaincode endorsement policy because of FAB-9473
for _, collRWSet := range nsRWSet.CollHashedRwSets {
if len(collRWSet.HashedRwSet.HashedReads) > 0 {
err := policyChecker.checkCCEP(cc, blockNum, txNum)
if err != nil {
return err
}
}
}
}
return nil

// we make sure that we check at least the CCEP to honour FAB-9473
return policyChecker.checkCCEPIfNoEPChecked(cc, blockNum, txNum)
}

// PostValidate implements the function of the StateBasedValidator interface
Expand Down
52 changes: 51 additions & 1 deletion core/common/validation/statebased/validator_keylevel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ import (
)

type mockPolicyEvaluator struct {
EvaluateRV error
EvaluateRV error
EvaluateResByPolicy map[string]error
}

func (m *mockPolicyEvaluator) Evaluate(policyBytes []byte, signatureSet []*common.SignedData) error {
if res, ok := m.EvaluateResByPolicy[string(policyBytes)]; ok {
return res
}

return m.EvaluateRV
}

Expand Down Expand Up @@ -334,6 +339,51 @@ func TestCCEPValidationReads(t *testing.T) {
assert.IsType(t, &errors.VSCCEndorsementPolicyError{}, err)
}

func TestOnlySBEPChecked(t *testing.T) {
t.Parallel()

// Scenario: we ensure that as long as there is one key that
// requires state-based endorsement, we only check that policy
// and we do not check the cc-EP. We check that by setting up the
// policy evaluator mock into returning an error for all policies
// but the state-based one, and expect successful evaluation

vpMetadataKey := pb.MetaDataKeys_VALIDATION_PARAMETER.String()
mr := &mockState{map[string][]byte{vpMetadataKey: []byte("SBEP")}, nil, map[string][]byte{}, nil, false}
ms := &mockStateFetcher{mr, nil}
pm := &KeyLevelValidationParameterManagerImpl{Support: ms}
pe := &mockPolicyEvaluator{}
validator := NewKeyLevelValidator(pe, pm)

rwsb := rwsetBytes(t, "cc")
prp := []byte("barf")
block := buildBlockWithTxs(buildTXWithRwset(rwsetUpdatingMetadataFor("cc", "key")), buildTXWithRwset(rwsetUpdatingMetadataFor("cc", "key")))

validator.PreValidate(1, block)

go func() {
validator.PostValidate("cc", 1, 0, fmt.Errorf(""))
}()

pe.EvaluateRV = fmt.Errorf("policy evaluation error")
pe.EvaluateResByPolicy = map[string]error{
"SBEP": nil,
}

err := validator.Validate("cc", 1, 1, rwsb, prp, []byte("CCEP"), []*pb.Endorsement{})
assert.NoError(t, err)

// we also test with a read-write set that has a read as well as a write
rwsbu := rwsetutil.NewRWSetBuilder()
rwsbu.AddToWriteSet("cc", "key", []byte("value"))
rwsbu.AddToReadSet("cc", "key", nil)
rws := rwsbu.GetTxReadWriteSet()
rwsb, _ = rws.ToProtoBytes()

err = validator.Validate("cc", 1, 1, rwsb, prp, []byte("CCEP"), []*pb.Endorsement{})
assert.NoError(t, err)
}

func TestCCEPValidationPvtReads(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 5388376

Please sign in to comment.