Skip to content

Commit b33bc2b

Browse files
committed
[FAB-11973] failing test in statebased validation
With [FAB-11948], retrieval of state from a collection may return an error in case the collection configuration isn't defined. The key-level validation code must handle this case without failing, given that failure in validation implies halting the processing on the chain. The chosen approach is to treat the error as a benign retrieval failure, crucially, one that will deterministically occur at every committing peer. As such, validation can continue as if that key didn't have state-based endorsement policies. The validator may therefore mark the transaction as valid, but the ledger will mark it as invalid given the attempt to write to an undefined collection. Change-Id: I2e5aaf0d05ec803873edcb92a3b9377e440b1ca1 Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net> Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com>
1 parent 757a4cf commit b33bc2b

File tree

6 files changed

+100
-29
lines changed

6 files changed

+100
-29
lines changed

core/committer/txvalidator/validator_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,6 @@ func TestInvokeOKPvtDataOnly(t *testing.T) {
538538
})
539539

540540
t.Run("V1.3", func(t *testing.T) {
541-
t.Skip()
542541
l, v := setupLedgerAndValidatorExplicitWithMSP(t, v13Capabilities(), &builtin.DefaultValidation{}, mspmgr)
543542
defer ledgermgmt.CleanupTestEnv()
544543
defer l.Close()
@@ -627,7 +626,6 @@ func TestInvokeOKPvtMetaUpdateOnly(t *testing.T) {
627626
})
628627

629628
t.Run("V1.3", func(t *testing.T) {
630-
t.Skip()
631629
l, v := setupLedgerAndValidatorExplicitWithMSP(t, &mockconfig.MockApplicationCapabilities{V1_3ValidationRv: true, V1_2ValidationRv: true, PrivateChannelDataRv: true}, &builtin.DefaultValidation{}, mspmgr)
632630
defer ledgermgmt.CleanupTestEnv()
633631
defer l.Close()

core/common/validation/statebased/validator_keylevel.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
commonerrors "github.com/hyperledger/fabric/common/errors"
1414
"github.com/hyperledger/fabric/core/handlers/validation/api/policies"
15+
"github.com/hyperledger/fabric/core/ledger"
1516
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
1617
"github.com/hyperledger/fabric/protos/common"
1718
"github.com/hyperledger/fabric/protos/peer"
@@ -59,9 +60,29 @@ func (p *policyChecker) checkSBAndCCEP(cc, coll, key string, blockNum, txNum uin
5960
// see if there is a key-level validation parameter for this key
6061
vp, err := p.vpmgr.GetValidationParameterForKey(cc, coll, key, blockNum, txNum)
6162
if err != nil {
62-
switch err := err.(type) {
63+
// error handling for GetValidationParameterForKey follows this rationale:
64+
switch err := errors.Cause(err).(type) {
65+
// 1) if there is a conflict because validation params have been updated
66+
// by another transaction in this block, we will get ValidationParameterUpdatedError.
67+
// This should lead to invalidating the transaction by calling policyErr
6368
case *ValidationParameterUpdatedError:
6469
return policyErr(err)
70+
// 2) if the ledger returns "determinstic" errors, that is, errors that
71+
// every peer in the channel will also return (such as errors linked to
72+
// an attempt to retrieve metadata from a non-defined collection) should be
73+
// logged and ignored. The ledger will take the most appropriate action
74+
// when performing its side of the validation.
75+
case *ledger.CollConfigNotDefinedError, *ledger.InvalidCollNameError:
76+
logger.Warningf(errors.WithMessage(err, "skipping key-level validation").Error())
77+
err = nil
78+
// 3) any other type of error should return an execution failure which will
79+
// lead to halting the processing on this channel. Note that any non-categorized
80+
// deterministic error would be caught by the default and would lead to
81+
// a processing halt. This would certainly be a bug, but - in the absence of a
82+
// single, well-defined deterministic error returned by the ledger, it is
83+
// best to err on the side of caution and rather halt processing (because a
84+
// deterministic error is treated like an I/O one) rather than risking a fork
85+
// (in case an I/O error is treated as a deterministic one).
6586
default:
6687
return &commonerrors.VSCCExecutionFailureError{
6788
Err: err,

core/common/validation/statebased/validator_keylevel_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"testing"
1212

1313
"github.com/hyperledger/fabric/common/errors"
14+
"github.com/hyperledger/fabric/core/ledger"
1415
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
1516
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
1617
"github.com/hyperledger/fabric/protos/common"
@@ -262,6 +263,48 @@ func TestKeylevelValidationPolicyRetrievalFailure(t *testing.T) {
262263
assert.IsType(t, &errors.VSCCExecutionFailureError{}, err)
263264
}
264265

266+
func TestKeylevelValidationLedgerFailures(t *testing.T) {
267+
// Scenario: we validate a transaction that updates
268+
// the key-level validation parameters for a key.
269+
// we simulate the case where we fail to retrieve
270+
// the validation parameters from the ledger with
271+
// both deterministic and non-deterministic errors
272+
273+
rwsb := rwsetBytes(t, "cc")
274+
prp := []byte("barf")
275+
276+
t.Run("CollConfigNotDefinedError", func(t *testing.T) {
277+
mr := &mockState{GetStateMetadataErr: &ledger.CollConfigNotDefinedError{Ns: "mycc"}}
278+
ms := &mockStateFetcher{FetchStateRv: mr}
279+
pm := &KeyLevelValidationParameterManagerImpl{StateFetcher: ms}
280+
validator := NewKeyLevelValidator(&mockPolicyEvaluator{}, pm)
281+
282+
err := validator.Validate("cc", 1, 0, rwsb, prp, []byte("CCEP"), []*pb.Endorsement{})
283+
assert.NoError(t, err)
284+
})
285+
286+
t.Run("InvalidCollNameError", func(t *testing.T) {
287+
mr := &mockState{GetStateMetadataErr: &ledger.InvalidCollNameError{Ns: "mycc", Coll: "mycoll"}}
288+
ms := &mockStateFetcher{FetchStateRv: mr}
289+
pm := &KeyLevelValidationParameterManagerImpl{StateFetcher: ms}
290+
validator := NewKeyLevelValidator(&mockPolicyEvaluator{}, pm)
291+
292+
err := validator.Validate("cc", 1, 0, rwsb, prp, []byte("CCEP"), []*pb.Endorsement{})
293+
assert.NoError(t, err)
294+
})
295+
296+
t.Run("I/O error", func(t *testing.T) {
297+
mr := &mockState{GetStateMetadataErr: fmt.Errorf("some I/O error")}
298+
ms := &mockStateFetcher{FetchStateRv: mr}
299+
pm := &KeyLevelValidationParameterManagerImpl{StateFetcher: ms}
300+
validator := NewKeyLevelValidator(&mockPolicyEvaluator{}, pm)
301+
302+
err := validator.Validate("cc", 1, 0, rwsb, prp, []byte("CCEP"), []*pb.Endorsement{})
303+
assert.Error(t, err)
304+
assert.IsType(t, &errors.VSCCExecutionFailureError{}, err)
305+
})
306+
}
307+
265308
func TestCCEPValidation(t *testing.T) {
266309
t.Parallel()
267310

core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val.go

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ SPDX-License-Identifier: Apache-2.0
66
package lockbasedtxmgr
77

88
import (
9-
"fmt"
10-
119
"github.com/golang/protobuf/proto"
1210
"github.com/hyperledger/fabric/core/common/privdata"
11+
"github.com/hyperledger/fabric/core/ledger"
1312
"github.com/hyperledger/fabric/protos/common"
1413
)
1514

@@ -38,7 +37,10 @@ func (v *collNameValidator) validateCollName(ns, coll string) error {
3837
v.cache.populate(ns, conf)
3938
}
4039
if !v.cache.containsCollName(ns, coll) {
41-
return &errInvalidCollName{ns, coll}
40+
return &ledger.InvalidCollNameError{
41+
Ns: ns,
42+
Coll: coll,
43+
}
4244
}
4345
logger.Debugf("validateCollName() validated successfully - ns=[%s], coll=[%s]", ns, coll)
4446
return nil
@@ -51,7 +53,7 @@ func (v *collNameValidator) retrieveCollConfigFromStateDB(ns string) (*common.Co
5153
return nil, err
5254
}
5355
if configPkgBytes == nil {
54-
return nil, &errCollConfigNotDefined{ns}
56+
return nil, &ledger.CollConfigNotDefinedError{Ns: ns}
5557
}
5658
confPkg := &common.CollectionConfigPackage{}
5759
if err := proto.Unmarshal(configPkgBytes, confPkg); err != nil {
@@ -91,19 +93,3 @@ func (c collConfigCache) containsCollName(ns, coll string) bool {
9193
func constructCollectionConfigKey(chaincodeName string) string {
9294
return privdata.BuildCollectionKVSKey(chaincodeName)
9395
}
94-
95-
type errInvalidCollName struct {
96-
ns, coll string
97-
}
98-
99-
func (e *errInvalidCollName) Error() string {
100-
return fmt.Sprintf("collection [%s] not defined in the collection config for chaincode [%s]", e.coll, e.ns)
101-
}
102-
103-
type errCollConfigNotDefined struct {
104-
ns string
105-
}
106-
107-
func (e *errCollConfigNotDefined) Error() string {
108-
return fmt.Sprintf("collection config not defined for chaincode [%s], pass the collection configuration upon chaincode definition/instantiation", e.ns)
109-
}

core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package lockbasedtxmgr
88
import (
99
"testing"
1010

11+
"github.com/hyperledger/fabric/core/ledger"
1112
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
1213
"github.com/stretchr/testify/assert"
1314
)
@@ -31,19 +32,19 @@ func TestCollectionValidation(t *testing.T) {
3132
assert.NoError(t, err)
3233

3334
_, err = sim.GetPrivateData("ns3", "coll1", "key1")
34-
_, ok := err.(*errCollConfigNotDefined)
35+
_, ok := err.(*ledger.CollConfigNotDefinedError)
3536
assert.True(t, ok)
3637

3738
err = sim.SetPrivateData("ns3", "coll1", "key1", []byte("val1"))
38-
_, ok = err.(*errCollConfigNotDefined)
39+
_, ok = err.(*ledger.CollConfigNotDefinedError)
3940
assert.True(t, ok)
4041

4142
_, err = sim.GetPrivateData("ns1", "coll3", "key1")
42-
_, ok = err.(*errInvalidCollName)
43+
_, ok = err.(*ledger.InvalidCollNameError)
4344
assert.True(t, ok)
4445

4546
err = sim.SetPrivateData("ns1", "coll3", "key1", []byte("val1"))
46-
_, ok = err.(*errInvalidCollName)
47+
_, ok = err.(*ledger.InvalidCollNameError)
4748
assert.True(t, ok)
4849

4950
err = sim.SetPrivateData("ns1", "coll1", "key1", []byte("val1"))
@@ -60,7 +61,7 @@ func TestPvtGetNoCollection(t *testing.T) {
6061
assert.Nil(t, valueHash)
6162
assert.Nil(t, metadataBytes)
6263
assert.Error(t, err)
63-
assert.IsType(t, &errCollConfigNotDefined{}, err)
64+
assert.IsType(t, &ledger.CollConfigNotDefinedError{}, err)
6465
}
6566

6667
func TestPvtPutNoCollection(t *testing.T) {
@@ -72,5 +73,5 @@ func TestPvtPutNoCollection(t *testing.T) {
7273
assert.NoError(t, err)
7374
err = txsim.SetPrivateDataMetadata("cc", "coll", "key", map[string][]byte{})
7475
assert.Error(t, err)
75-
assert.IsType(t, &errCollConfigNotDefined{}, err)
76+
assert.IsType(t, &ledger.CollConfigNotDefinedError{}, err)
7677
}

core/ledger/ledger_interface.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
77
package ledger
88

99
import (
10+
"fmt"
11+
1012
"github.com/golang/protobuf/proto"
1113
commonledger "github.com/hyperledger/fabric/common/ledger"
1214
"github.com/hyperledger/fabric/protos/common"
@@ -418,6 +420,26 @@ func (NotFoundInIndexErr) Error() string {
418420
return "Entry not found in index"
419421
}
420422

423+
// CollConfigNotDefinedError is returned whenever an operation
424+
// is requested on a collection whose config has not been defined
425+
type CollConfigNotDefinedError struct {
426+
Ns string
427+
}
428+
429+
func (e *CollConfigNotDefinedError) Error() string {
430+
return fmt.Sprintf("collection config not defined for chaincode [%s], pass the collection configuration upon chaincode definition/instantiation", e.Ns)
431+
}
432+
433+
// InvalidCollNameError is returned whenever an operation
434+
// is requested on a collection whose name is invalid
435+
type InvalidCollNameError struct {
436+
Ns, Coll string
437+
}
438+
439+
func (e *InvalidCollNameError) Error() string {
440+
return fmt.Sprintf("collection [%s] not defined in the collection config for chaincode [%s]", e.Coll, e.Ns)
441+
}
442+
421443
// PvtdataHashMismatch is used when the hash of private write-set
422444
// does not match the corresponding hash present in the block
423445
// See function `PeerLedger.CommitPvtData` for the usages

0 commit comments

Comments
 (0)