Skip to content

Commit

Permalink
history, performance, deletion, filtering, date ranges, bug fixes
Browse files Browse the repository at this point in the history
— implemented asset deletion, delete all, filtered delete all
— improved filters, cleaned up a few bugs
— rewrote history processing completely
  — stores timestamped history in individual records
  — implemented read history by date range with filtering
— improved recent states algorithms
— improved contains algorithm
  • Loading branch information
kletkeman committed Nov 13, 2016
1 parent 0d7fa97 commit 9e6438e
Show file tree
Hide file tree
Showing 15 changed files with 1,284 additions and 266 deletions.
71 changes: 23 additions & 48 deletions contracts/platform/iotcontractplatform/ctasset.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (c *AssetClass) DeleteAsset(stub shim.ChaincodeStubInterface, args []string
log.Errorf(err.Error())
return nil, err
}
err = removeOneAssetFromWorldState(stub, assetKey)
err = arg.removeOneAssetFromWorldState(stub)
if err != nil {
err := fmt.Errorf("DeleteAsset: removeOneAssetFromWorldState class %s, asset %s, returned error: %s", c.Name, assetKey, err)
log.Errorf(err.Error())
Expand All @@ -243,7 +243,12 @@ func (c *AssetClass) DeleteAsset(stub shim.ChaincodeStubInterface, args []string
func (c *AssetClass) DeleteAllAssets(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
var filter StateFilter

filter = getUnmarshalledStateFilter(stub, "DeleteAllAssets", args)
filter, err := getUnmarshalledStateFilter(stub, args)
if err != nil {
err = fmt.Errorf("DeleteAllAssets failed to get the filter: %s", err)
log.Error(err)
return nil, err
}
iter, err := stub.RangeQueryState(c.Prefix, c.Prefix+"}")
if err != nil {
err = fmt.Errorf("DeleteAllAssets failed to get a range query iterator: %s", err)
Expand All @@ -265,8 +270,8 @@ func (c *AssetClass) DeleteAllAssets(stub shim.ChaincodeStubInterface, args []st
log.Errorf(err.Error())
return nil, err
}
if len(filter.Entries) == 0 || state.Filter(filter) {
err = removeOneAssetFromWorldState(stub, key)
if state.Filter(filter) {
err = state.removeOneAssetFromWorldState(stub)
if err != nil {
err = fmt.Errorf("DeleteAllAssets removeOneAssetFromWorldState for asset %s failed: %s", key, err)
log.Errorf(err.Error())
Expand Down Expand Up @@ -410,7 +415,12 @@ func (c AssetClass) ReadAllAssetsUnmarshalled(stub shim.ChaincodeStubInterface,
var err error
var filter StateFilter

filter = getUnmarshalledStateFilter(stub, "ReadAllAssetsUnmarshalled", args)
filter, err = getUnmarshalledStateFilter(stub, args)
if err != nil {
err = fmt.Errorf("readAllAssetsUnmarshalled failed to get a filter: %s", err)
log.Errorf(err.Error())
return nil, err
}

iter, err := stub.RangeQueryState(c.Prefix, c.Prefix+"}")
if err != nil {
Expand All @@ -433,7 +443,7 @@ func (c AssetClass) ReadAllAssetsUnmarshalled(stub shim.ChaincodeStubInterface,
log.Errorf(err.Error())
return nil, err
}
if len(filter.Entries) == 0 || state.Filter(filter) {
if state.Filter(filter) {
assets = append(assets, *state)
}
}
Expand All @@ -447,50 +457,15 @@ func (c AssetClass) ReadAllAssetsUnmarshalled(stub shim.ChaincodeStubInterface,
return assets, nil
}

// // ReadAssetHistory returns an asset's history from world state as an array
// func ReadAssetHistory(stub shim.ChaincodeStubInterface, args []string, assetName string, caller string) ([]byte, error) {
// argsMap, err := getUnmarshalledArgument(stub, caller, args)
// if err != nil {
// return nil, err
// }
// assetID, err := validateAssetID(caller, assetName, argsMap)
// if err != nil {
// return nil, err
// }
// stateHistory, err := h.ReadStateHistory(stub, assetID)
// if err != nil {
// return nil, err
// }
// // is count present?
// var olen int
// countBytes, found := GetObject(argsMap, "count")
// if found {
// olen = int(countBytes.(float64))
// }
// if olen <= 0 || olen > len(stateHistory.AssetHistory) {
// olen = len(stateHistory.AssetHistory)
// }
// var hStatesOut = make([]interface{}, 0, olen)
// for i := 0; i < olen; i++ {
// var obj interface{}
// err = json.Unmarshal([]byte(stateHistory.AssetHistory[i]), &obj)
// if err != nil {
// log.Errorf("readAssetHistory JSON unmarshal of entry %d failed [%#v]", i, stateHistory.AssetHistory[i])
// return nil, err
// }
// hStatesOut = append(hStatesOut, obj)
// }
// assetBytes, err := json.Marshal(hStatesOut)
// if err != nil {
// log.Errorf("readAssetHistory failed to marshal results: %s", err)
// return nil, err
// }

// return []byte(assetBytes), nil
// }

//********** sort interface for AssetArray

func (aa AssetArray) Len() int { return len(aa) }
func (aa AssetArray) Swap(i, j int) { aa[i], aa[j] = aa[j], aa[i] }
func (aa AssetArray) Less(i, j int) bool { return aa[i].AssetKey < aa[j].AssetKey }

// ByTimestamp alias for sorting by timestamp
type ByTimestamp AssetArray

func (aa ByTimestamp) Len() int { return len(aa) }
func (aa ByTimestamp) Swap(i, j int) { aa[i], aa[j] = aa[j], aa[i] }
func (aa ByTimestamp) Less(i, j int) bool { return (*aa[i].TXNTS).Before(*aa[j].TXNTS) }
2 changes: 1 addition & 1 deletion contracts/platform/iotcontractplatform/ctcontractstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

// CONTRACTSTATEKEY is used to store contract state, including version, nickname and activeAssets
const CONTRACTSTATEKEY string = "IOTCP:ContractStateKey"
const CONTRACTSTATEKEY string = "IOTCP:ContractState"

// ContractState struct defines contract state. Unlike the main contract maps, structs work fine
// for this fixed structure.
Expand Down
35 changes: 17 additions & 18 deletions contracts/platform/iotcontractplatform/ctcrud.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,36 +207,35 @@ func (a *Asset) putMarshalledState(stub shim.ChaincodeStubInterface) error {
return err
}

// // add history state
// err = h.UpdateStateHistory(stub, assetID, string(stateJSON))
// if err != nil {
// err = fmt.Errorf("%s: event %s assetID %s push history failed: %s", caller, eventName, assetID, err)
// log.Errorf(err.Error())
// return err
// }
err = a.PUTAssetStateHistory(stub)
if err != nil {
err = fmt.Errorf("putMarshalledState failed to put asset %s history: %s", a.AssetKey, err)
log.Error(err)
return err
}
return nil
}

// RemoveOneAssetFromWorldState remove the asset from world state
func removeOneAssetFromWorldState(stub shim.ChaincodeStubInterface, assetKey string) error {
err := stub.DelState(assetKey)
func (a *Asset) removeOneAssetFromWorldState(stub shim.ChaincodeStubInterface) error {
err := stub.DelState(a.AssetKey)
if err != nil {
err = fmt.Errorf("removeOneAssetFromWorldState: asset %s failed", a.AssetKey)
log.Error(err)
return err
}
err = a.RemoveAssetFromRecentStates(stub)
if err != nil {
err = fmt.Errorf("removeOneAssetFromWorldState: asset %s failed", assetKey)
err = fmt.Errorf("removeOneAssetFromWorldState: asset %s could not be removed from recent states: %s", a.AssetKey, err)
log.Error(err)
return err
}
err = RemoveAssetFromRecentStates(stub, assetKey)
err = a.DeleteAssetStateHistory(stub)
if err != nil {
err = fmt.Errorf("removeOneAssetFromWorldState: asset %s could not be removed from recent states: %s", assetKey, err)
err = fmt.Errorf("putMarshalledState failed to put asset %s history: %s", a.AssetKey, err)
log.Error(err)
return err
}
// err = h.DeleteStateHistory(stub, assetID)
// if err != nil {
// err = fmt.Errorf("%s: %s assetID %s history deletion failed", caller, assetName, assetID)
// log.Error(err)
// return err
// }
return nil
}

Expand Down
73 changes: 46 additions & 27 deletions contracts/platform/iotcontractplatform/ctfilters.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,33 @@ import (
type MatchType int32

const (
// MatchDisabled causes the filter to not execute
MatchDisabled = 0
// MatchAll requires that every property in the filter be present and have
// the same value
MatchAll MatchType = 0
MatchAll MatchType = 1
// MatchAny requires that at least one property in the filter be present and have
// the same value
MatchAny MatchType = 1
MatchAny MatchType = 2
// MatchNone requires that every property in the filter either be present and have
// a different value. or not be present
MatchNone MatchType = 2
MatchNone MatchType = 3
)

// MatchName is a map of ID to name
var MatchName = map[int]string{
0: "ALL",
1: "ANY",
2: "NONE",
0: "n/a",
1: "all",
2: "any",
3: "none",
}

// MatchValue is a map of name to ID
var MatchValue = map[string]int32{
"ALL": 0,
"ANY": 1,
"NONE": 2,
"n/a": 0,
"all": 1,
"any": 2,
"none": 3,
}

func (x MatchType) String() string {
Expand All @@ -67,22 +71,37 @@ type QPropNV struct {
Value string `json:"value"`
}

// StateFilter is an array of QPropNV
// FilterGroup is a matchmode with a list of K:V pairs
type FilterGroup struct {
Match string `json:"match"`
Select []QPropNV `json:"select"`
}

// StateFilter is a complete filter for a state
type StateFilter struct {
MatchMode string `json:"match"`
Entries []QPropNV `json:"select"`
Filter FilterGroup `json:"filter"`
}

var emptyFilter = StateFilter{"matchall", make([]QPropNV, 0)}
var emptyFilter = StateFilter{
FilterGroup{
"n/a",
make([]QPropNV, 0),
},
}

// Filter returns true if the filter's conditions are all met
func (a *Asset) Filter(filter StateFilter) bool {
switch filter.MatchMode {
case "ALL":
if len(filter.Filter.Select) == 0 {
return true
}
switch filter.Filter.Match {
case "n/a":
return true
case "all":
return matchAll(a, filter)
case "ANY":
case "any":
return matchAny(a, filter)
case "NONE":
case "none":
return matchNone(a, filter)
default:
err := fmt.Errorf("filterObject has unknown matchType in filter: %+v", filter)
Expand All @@ -92,7 +111,7 @@ func (a *Asset) Filter(filter StateFilter) bool {
}

func matchAll(a *Asset, filter StateFilter) bool {
for _, f := range filter.Entries {
for _, f := range filter.Filter.Select {
if !performOneMatch(a.State, f) {
// must match all
return false
Expand All @@ -103,7 +122,7 @@ func matchAll(a *Asset, filter StateFilter) bool {
}

func matchAny(a *Asset, filter StateFilter) bool {
for _, f := range filter.Entries {
for _, f := range filter.Filter.Select {
if performOneMatch(a.State, f) {
// must match at least one
return true
Expand All @@ -114,7 +133,7 @@ func matchAny(a *Asset, filter StateFilter) bool {
}

func matchNone(a *Asset, filter StateFilter) bool {
for _, f := range filter.Entries {
for _, f := range filter.Filter.Select {
if performOneMatch(a.State, f) {
// must not match any
return false
Expand Down Expand Up @@ -164,23 +183,23 @@ func performOneMatch(obj *map[string]interface{}, prop QPropNV) bool {
return false
}

// Returns a map containing the JSON object represented by args[0]
func getUnmarshalledStateFilter(stub shim.ChaincodeStubInterface, caller string, args []string) StateFilter {
// Returns a filter found in the json object in args[0]
func getUnmarshalledStateFilter(stub shim.ChaincodeStubInterface, args []string) (StateFilter, error) {
var filter StateFilter
var err error

if len(args) != 1 {
// perfectly normal to not have a filter
return emptyFilter
return emptyFilter, nil
}

fBytes := []byte(args[0])
err = json.Unmarshal(fBytes, &filter)
if err != nil {
err = fmt.Errorf("%s failed to unmarshal filter: %s error: %s", caller, args[0], err)
log.Errorf(err.Error())
return emptyFilter
err = fmt.Errorf("getUnmarshalledStateFilter failed to unmarshal %s as filter, error: %s", args[0], err)
log.Error(err)
return emptyFilter, err
}

return filter
return filter, nil
}
Loading

0 comments on commit 9e6438e

Please sign in to comment.