From c06fb8295f7e671890280f3c7ccdae033f6979fa Mon Sep 17 00:00:00 2001 From: Victor Nosov Date: Wed, 26 Jun 2019 23:11:12 +0300 Subject: [PATCH] fix history for mapped state (#19) --- README.md | 1 + state/mapping/state.go | 74 ++++++++++++++---------------- state/mapping/state_mapping.go | 1 + state/mapping/state_mapping_opt.go | 17 ++++++- state/state.go | 30 ++++++------ 5 files changed, 67 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index d7dcca83..dcfb95b8 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ and [others](docs/chaincode-examples.md) ### Publications with usage examples * [Hyperledger Fabric smart contract data model: protobuf to chaincode state mapping](https://medium.com/coinmonks/hyperledger-fabric-smart-contract-data-model-protobuf-to-chaincode-state-mapping-191cdcfa0b78) +* [Hyperledger Fabric chaincode test driven development (TDD) with unit testing](https://medium.com/coinmonks/test-driven-hyperledger-fabric-golang-chaincode-development-dbec4cb78049) * [ERC20 token as Hyperledger Fabric Golang chaincode](https://medium.com/@viktornosov/erc20-token-as-hyperledger-fabric-golang-chaincode-d09dfd16a339) * [CCKit: Routing and middleware for Hyperledger Fabric Golang chaincode](https://medium.com/@viktornosov/routing-and-middleware-for-developing-hyperledger-fabric-chaincode-written-in-go-90913951bf08) * [Developing and testing Hyperledger Fabric smart contracts](https://habr.com/post/426705/) [RUS] diff --git a/state/mapping/state.go b/state/mapping/state.go index e3667747..c5820f88 100644 --- a/state/mapping/state.go +++ b/state/mapping/state.go @@ -45,7 +45,7 @@ func (s *Impl) MappingNamespace(schema interface{}) (state.Key, error) { return m.Namespace(), nil } -func (s *Impl) Get(entry interface{}, target ...interface{}) (result interface{}, err error) { +func (s *Impl) Get(entry interface{}, target ...interface{}) (interface{}, error) { mapped, err := s.mappings.Map(entry) if err != nil { // mapping is not exists return s.state.Get(entry, target...) // return as is @@ -54,15 +54,20 @@ func (s *Impl) Get(entry interface{}, target ...interface{}) (result interface{} return s.state.Get(mapped, target...) } -func (s *Impl) GetInt(key interface{}, defaultValue int) (result int, err error) { - return s.state.GetInt(key, defaultValue) +func (s *Impl) GetInt(entry interface{}, defaultValue int) (int, error) { + return s.state.GetInt(entry, defaultValue) } -func (s *Impl) GetHistory(key interface{}, target interface{}) (result state.HistoryEntryList, err error) { - return s.state.GetHistory(key, target) +func (s *Impl) GetHistory(entry interface{}, target interface{}) (state.HistoryEntryList, error) { + mapped, err := s.mappings.Map(entry) + if err != nil { // mapping is not exists + return s.state.GetHistory(entry, target) // return as is + } + + return s.state.GetHistory(mapped, target) } -func (s *Impl) Exists(entry interface{}) (exists bool, err error) { +func (s *Impl) Exists(entry interface{}) (bool, error) { mapped, err := s.mappings.Map(entry) if err != nil { // mapping is not exists return s.state.Exists(entry) // return as is @@ -71,7 +76,7 @@ func (s *Impl) Exists(entry interface{}) (exists bool, err error) { return s.state.Exists(mapped) } -func (s *Impl) Put(entry interface{}, value ...interface{}) (err error) { +func (s *Impl) Put(entry interface{}, value ...interface{}) error { mapped, err := s.mappings.Map(entry) if err != nil { // mapping is not exists return s.state.Put(entry, value...) // return as is @@ -79,7 +84,7 @@ func (s *Impl) Put(entry interface{}, value ...interface{}) (err error) { keyRefs, err := mapped.Keys() // additional keys if err != nil { - return + return err } // delete previous key refs if key exists @@ -94,7 +99,7 @@ func (s *Impl) Put(entry interface{}, value ...interface{}) (err error) { return s.state.Put(mapped) } -func (s *Impl) Insert(entry interface{}, value ...interface{}) (err error) { +func (s *Impl) Insert(entry interface{}, value ...interface{}) error { mapped, err := s.mappings.Map(entry) if err != nil { // mapping is not exists return s.state.Insert(entry, value...) // return as is @@ -102,7 +107,7 @@ func (s *Impl) Insert(entry interface{}, value ...interface{}) (err error) { keyRefs, err := mapped.Keys() // additional keys if err != nil { - return + return err } // insert uniq key refs. if key already exists - error returned @@ -115,27 +120,20 @@ func (s *Impl) Insert(entry interface{}, value ...interface{}) (err error) { return s.state.Insert(mapped) } -func (s *Impl) List(namespace interface{}, target ...interface{}) (result interface{}, err error) { - if s.mappings.Exists(namespace) { - m, err := s.mappings.Get(namespace) - if err != nil { - return nil, errors.Wrap(err, `mapping`) - } +func (s *Impl) List(entry interface{}, target ...interface{}) (interface{}, error) { + if !s.mappings.Exists(entry) { + return s.state.List(entry, target...) + } - namespace = m.Namespace() - s.Logger().Debugf(`state mapped LIST with namespace: %s`, namespace) - target = targetFromMapping(m) + m, err := s.mappings.Get(entry) + if err != nil { + return nil, errors.Wrap(err, `mapping`) } - return s.state.List(namespace, target...) -} + namespace := m.Namespace() + s.Logger().Debugf(`state mapped LIST with namespace: %s`, namespace) -func targetFromMapping(m StateMapper) (target []interface{}) { - target = []interface{}{m.Schema()} - if list := m.List(); list != nil { - target = append(target, list) - } - return + return s.state.List(namespace, m.Schema(), m.List()) } func (s *Impl) ListWith(entry interface{}, key state.Key) (result interface{}, err error) { @@ -150,7 +148,7 @@ func (s *Impl) ListWith(entry interface{}, key state.Key) (result interface{}, e namespace := m.Namespace() s.Logger().Debugf(`state mapped LIST with namespace: %s`, namespace, namespace.Append(key)) - return s.state.List(namespace.Append(key), targetFromMapping(m)...) + return s.state.List(namespace.Append(key), m.Schema(), m.List()) } func (s *Impl) GetByUniqKey( @@ -169,7 +167,6 @@ func (s *Impl) GetByUniqKey( } func (s *Impl) Delete(entry interface{}) (err error) { - mapped, err := s.mappings.Map(entry) if err != nil { // mapping is not exists return s.state.Delete(entry) // return as is @@ -214,18 +211,17 @@ func (s *Impl) DeletePrivate(collection string, entry interface{}) (err error) { } func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, namespace interface{}, target ...interface{}) (result interface{}, err error) { - if s.mappings.Exists(namespace) { - m, err := s.mappings.Get(namespace) - if err != nil { - return nil, errors.Wrap(err, `mapping`) - } - - namespace = m.Namespace() - s.Logger().Debugf(`private state mapped LIST with namespace: %s`, namespace) - target = targetFromMapping(m) + if !s.mappings.Exists(namespace) { + return s.state.ListPrivate(collection, usePrivateDataIterator, namespace, target...) + } + m, err := s.mappings.Get(namespace) + if err != nil { + return nil, errors.Wrap(err, `mapping`) } - return s.state.ListPrivate(collection, usePrivateDataIterator, namespace, target...) + namespace = m.Namespace() + s.Logger().Debugf(`private state mapped LIST with namespace: %s`, namespace) + return s.state.ListPrivate(collection, usePrivateDataIterator, namespace, target[0], m.List()) } func (s *Impl) InsertPrivate(collection string, entry interface{}, value ...interface{}) (err error) { diff --git a/state/mapping/state_mapping.go b/state/mapping/state_mapping.go index ca70b631..60e17635 100644 --- a/state/mapping/state_mapping.go +++ b/state/mapping/state_mapping.go @@ -28,6 +28,7 @@ type ( Keys(instance interface{}) (key []state.KeyValue, err error) } + // InstanceKeyer returns key of an state entry instance InstanceKeyer func(instance interface{}) (key state.Key, err error) StateMapped interface { diff --git a/state/mapping/state_mapping_opt.go b/state/mapping/state_mapping_opt.go index 2a1ed4bd..978acee3 100644 --- a/state/mapping/state_mapping_opt.go +++ b/state/mapping/state_mapping_opt.go @@ -58,14 +58,29 @@ func PKeyAttr(attrs ...string) StateMappingOpt { } } +// PKeyId use Id attr as source for mapped state entry key func PKeyId() StateMappingOpt { return PKeyAttr(`Id`) } +// PKeyConst use constant as state entry key +func PKeyConst(key state.Key) StateMappingOpt { + return func(sm *StateMapping, smm StateMappings) { + sm.primaryKeyer = func(instance interface{}) (state.Key, error) { + return key, nil + } + } +} + +// PKeyComplexId sets Id as key field, also adds mapping for pkeySchema +// with namespace from mapping schema func PKeyComplexId(pkeySchema interface{}) StateMappingOpt { return func(sm *StateMapping, smm StateMappings) { sm.primaryKeyer = attrsPKeyer([]string{`Id`}) - smm.Add(pkeySchema, StateNamespace(SchemaNamespace(sm.schema)), PKeyAttr(attrsFrom(pkeySchema)...), IsKeyerSchema()) + smm.Add(pkeySchema, + StateNamespace(SchemaNamespace(sm.schema)), + PKeyAttr(attrsFrom(pkeySchema)...), + IsKeyerSchema()) } } diff --git a/state/state.go b/state/state.go index 66908b81..31750cd1 100644 --- a/state/state.go +++ b/state/state.go @@ -180,7 +180,7 @@ func (s *Impl) Key(key interface{}) (string, error) { } // Get data by key from state, trying to convert to target interface -func (s *Impl) Get(key interface{}, config ...interface{}) (result interface{}, err error) { +func (s *Impl) Get(key interface{}, config ...interface{}) (interface{}, error) { strKey, err := s.Key(key) if err != nil { return nil, err @@ -190,7 +190,7 @@ func (s *Impl) Get(key interface{}, config ...interface{}) (result interface{}, s.logger.Debugf(`state GET %s`, strKey) bb, err := s.stub.GetState(strKey) if err != nil { - return + return nil, err } if len(bb) == 0 { // config[1] default value @@ -204,7 +204,7 @@ func (s *Impl) Get(key interface{}, config ...interface{}) (result interface{}, return s.StateGetTransformer(bb, config...) } -func (s *Impl) GetInt(key interface{}, defaultValue int) (result int, err error) { +func (s *Impl) GetInt(key interface{}, defaultValue int) (int, error) { val, err := s.Get(key, convert.TypeInt, defaultValue) if err != nil { return 0, err @@ -213,7 +213,7 @@ func (s *Impl) GetInt(key interface{}, defaultValue int) (result int, err error) } // GetHistory by key from state, trying to convert to target interface -func (s *Impl) GetHistory(key interface{}, target interface{}) (result HistoryEntryList, err error) { +func (s *Impl) GetHistory(key interface{}, target interface{}) (HistoryEntryList, error) { strKey, err := s.Key(key) if err != nil { return nil, err @@ -251,7 +251,7 @@ func (s *Impl) GetHistory(key interface{}, target interface{}) (result HistoryEn } // Exists check entry with key exists in chaincode state -func (s *Impl) Exists(entry interface{}) (exists bool, err error) { +func (s *Impl) Exists(entry interface{}) (bool, error) { strKey, err := s.Key(entry) if err != nil { return false, err @@ -266,8 +266,7 @@ func (s *Impl) Exists(entry interface{}) (exists bool, err error) { // List data from state using objectType prefix in composite key, trying to convert to target interface. // Keys - additional components of composite key -func (s *Impl) List(namespace interface{}, target ...interface{}) (result interface{}, err error) { - +func (s *Impl) List(namespace interface{}, target ...interface{}) (interface{}, error) { stateList, err := NewStateList(target...) if err != nil { return nil, err @@ -311,8 +310,7 @@ func NormalizeStateKey(key interface{}) (Key, error) { func (s *Impl) argKeyValue(arg interface{}, values []interface{}) (key Key, value interface{}, err error) { // key must be - key, err = NormalizeStateKey(arg) - if err != nil { + if key, err = NormalizeStateKey(arg); err != nil { return } @@ -328,7 +326,7 @@ func (s *Impl) argKeyValue(arg interface{}, values []interface{}) (key Key, valu } // Put data value in state with key, trying convert data to []byte -func (s *Impl) Put(entry interface{}, values ...interface{}) (err error) { +func (s *Impl) Put(entry interface{}, values ...interface{}) error { key, value, err := s.argKeyValue(entry, values) if err != nil { return err @@ -348,7 +346,7 @@ func (s *Impl) Put(entry interface{}, values ...interface{}) (err error) { } // Insert value into chaincode state, returns error if key already exists -func (s *Impl) Insert(entry interface{}, values ...interface{}) (err error) { +func (s *Impl) Insert(entry interface{}, values ...interface{}) error { if exists, err := s.Exists(entry); err != nil { return err } else if exists { @@ -364,7 +362,7 @@ func (s *Impl) Insert(entry interface{}, values ...interface{}) (err error) { } // Delete entry from state -func (s *Impl) Delete(key interface{}) (err error) { +func (s *Impl) Delete(key interface{}) error { strKey, err := s.Key(key) if err != nil { return errors.Wrap(err, `deleting from state`) @@ -408,7 +406,7 @@ func KeyError(strKey string) error { //} // Get data by key from private state, trying to convert to target interface -func (s *Impl) GetPrivate(collection string, key interface{}, config ...interface{}) (result interface{}, err error) { +func (s *Impl) GetPrivate(collection string, key interface{}, config ...interface{}) (interface{}, error) { strKey, err := s.Key(key) if err != nil { return nil, err @@ -418,7 +416,7 @@ func (s *Impl) GetPrivate(collection string, key interface{}, config ...interfac s.logger.Debugf(`private state GET %s`, strKey) bb, err := s.stub.GetPrivateData(collection, strKey) if err != nil { - return + return nil, err } if len(bb) == 0 { // config[1] default value @@ -433,7 +431,7 @@ func (s *Impl) GetPrivate(collection string, key interface{}, config ...interfac } // PrivateExists check entry with key exists in chaincode private state -func (s *Impl) ExistsPrivate(collection string, entry interface{}) (exists bool, err error) { +func (s *Impl) ExistsPrivate(collection string, entry interface{}) (bool, error) { strKey, err := s.Key(entry) if err != nil { return false, err @@ -540,7 +538,7 @@ func (s *Impl) InsertPrivate(collection string, entry interface{}, values ...int } // Delete entry from private state -func (s *Impl) DeletePrivate(collection string, key interface{}) (err error) { +func (s *Impl) DeletePrivate(collection string, key interface{}) error { strKey, err := s.Key(key) if err != nil { return errors.Wrap(err, `deleting from private state`)