Skip to content

Commit 91eefe1

Browse files
authored
Merge pull request #17 from GoLedgerDev/develop
Dynamic Asset Type funcionality
2 parents 128f971 + f9c62d4 commit 91eefe1

23 files changed

+2941
-0
lines changed

assets/assetList.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
package assets
22

3+
import "time"
4+
35
// assetTypeList is the list which should contain all defined asset types
46
var assetTypeList = []AssetType{}
7+
8+
// listUpdateTime is the last time the assetTypeList was updated
9+
var listUpdateTime time.Time

assets/assetListFuncs.go

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package assets
22

3+
import (
4+
"net/http"
5+
"time"
6+
7+
"github.com/goledgerdev/cc-tools/errors"
8+
sw "github.com/goledgerdev/cc-tools/stubwrapper"
9+
)
10+
311
// AssetTypeList returns a copy of the assetTypeList variable.
412
func AssetTypeList() []AssetType {
513
listCopy := make([]AssetType, len(assetTypeList))
@@ -19,5 +27,194 @@ func FetchAssetType(assetTypeTag string) *AssetType {
1927

2028
// InitAssetList appends custom assets to assetTypeList to avoid initialization loop.
2129
func InitAssetList(l []AssetType) {
30+
if GetEnabledDynamicAssetType() {
31+
l = append(l, GetListAssetType())
32+
}
33+
assetTypeList = l
34+
}
35+
36+
// ReplaceAssetList replace assetTypeList to for a new one
37+
func ReplaceAssetList(l []AssetType) {
2238
assetTypeList = l
2339
}
40+
41+
// UpdateAssetList updates the assetTypeList variable on runtime
42+
func UpdateAssetList(l []AssetType) {
43+
assetTypeList = append(assetTypeList, l...)
44+
}
45+
46+
// RemoveAssetType removes an asset type from an AssetType List and returns the new list
47+
func RemoveAssetType(tag string, l []AssetType) []AssetType {
48+
for i, assetType := range l {
49+
if assetType.Tag == tag {
50+
l = append(l[:i], l[i+1:]...)
51+
}
52+
}
53+
return l
54+
}
55+
56+
// ReplaceAssetType replaces an asset type from an AssetType List with an updated version and returns the new list
57+
// This function does not automatically update the assetTypeList variable
58+
func ReplaceAssetType(assetType AssetType, l []AssetType) []AssetType {
59+
for i, v := range l {
60+
if v.Tag == assetType.Tag {
61+
l[i] = assetType
62+
}
63+
}
64+
return l
65+
}
66+
67+
// StoreAssetList stores the current assetList on the blockchain
68+
func StoreAssetList(stub *sw.StubWrapper) errors.ICCError {
69+
assetList := AssetTypeList()
70+
l := ArrayFromAssetTypeList(assetList)
71+
72+
txTimestamp, err := stub.Stub.GetTxTimestamp()
73+
if err != nil {
74+
return errors.WrapError(err, "failed to get tx timestamp")
75+
}
76+
txTime := txTimestamp.AsTime()
77+
txTimeStr := txTime.Format(time.RFC3339)
78+
79+
listKey, err := NewKey(map[string]interface{}{
80+
"@assetType": "assetTypeListData",
81+
"id": "primary",
82+
})
83+
if err != nil {
84+
return errors.NewCCError("error getting asset list key", http.StatusInternalServerError)
85+
}
86+
87+
exists, err := listKey.ExistsInLedger(stub)
88+
if err != nil {
89+
return errors.NewCCError("error checking if asset list exists", http.StatusInternalServerError)
90+
}
91+
92+
if exists {
93+
listAsset, err := listKey.Get(stub)
94+
if err != nil {
95+
return errors.WrapError(err, "error getting asset list")
96+
}
97+
listMap := (map[string]interface{})(*listAsset)
98+
99+
listMap["list"] = l
100+
listMap["lastUpdated"] = txTimeStr
101+
102+
_, err = listAsset.Update(stub, listMap)
103+
if err != nil {
104+
return errors.WrapError(err, "error updating asset list")
105+
}
106+
} else {
107+
listMap := map[string]interface{}{
108+
"@assetType": "assetTypeListData",
109+
"id": "primary",
110+
"list": l,
111+
"lastUpdated": txTimeStr,
112+
}
113+
114+
listAsset, err := NewAsset(listMap)
115+
if err != nil {
116+
return errors.WrapError(err, "error creating asset list")
117+
}
118+
119+
_, err = listAsset.PutNew(stub)
120+
if err != nil {
121+
return errors.WrapError(err, "error putting asset list")
122+
}
123+
}
124+
125+
SetAssetListUpdateTime(txTime)
126+
127+
return nil
128+
}
129+
130+
// RestoreAssetList restores the assetList from the blockchain
131+
func RestoreAssetList(stub *sw.StubWrapper, init bool) errors.ICCError {
132+
listKey, err := NewKey(map[string]interface{}{
133+
"@assetType": "assetTypeListData",
134+
"id": "primary",
135+
})
136+
if err != nil {
137+
return errors.NewCCError("error gettin asset list key", http.StatusInternalServerError)
138+
}
139+
140+
exists, err := listKey.ExistsInLedger(stub)
141+
if err != nil {
142+
return errors.NewCCError("error checking if asset list exists", http.StatusInternalServerError)
143+
}
144+
145+
if exists {
146+
listAsset, err := listKey.Get(stub)
147+
if err != nil {
148+
return errors.NewCCError("error getting asset list", http.StatusInternalServerError)
149+
}
150+
listMap := (map[string]interface{})(*listAsset)
151+
152+
txTime := listMap["lastUpdated"].(time.Time)
153+
154+
if GetAssetListUpdateTime().After(txTime) {
155+
return nil
156+
}
157+
158+
l := AssetTypeListFromArray(listMap["list"].([]interface{}))
159+
160+
l = getRestoredList(l, init)
161+
162+
ReplaceAssetList(l)
163+
164+
SetAssetListUpdateTime(txTime)
165+
}
166+
167+
return nil
168+
}
169+
170+
// getRestoredList reconstructs the assetTypeList from the stored list comparing to the current list
171+
func getRestoredList(storedList []AssetType, init bool) []AssetType {
172+
assetList := AssetTypeList()
173+
174+
deleteds := AssetTypeList()
175+
176+
for _, assetTypeStored := range storedList {
177+
if !assetTypeStored.Dynamic {
178+
continue
179+
}
180+
181+
exists := false
182+
for i, assetType := range assetList {
183+
if assetType.Tag == assetTypeStored.Tag {
184+
exists = true
185+
186+
assetTypeStored.Validate = assetType.Validate
187+
assetList[i] = assetTypeStored
188+
189+
deleteds = RemoveAssetType(assetType.Tag, deleteds)
190+
191+
break
192+
}
193+
}
194+
if !exists {
195+
assetList = append(assetList, assetTypeStored)
196+
}
197+
}
198+
199+
if !init {
200+
for _, deleted := range deleteds {
201+
if !deleted.Dynamic {
202+
continue
203+
}
204+
205+
assetList = RemoveAssetType(deleted.Tag, assetList)
206+
}
207+
}
208+
209+
return assetList
210+
}
211+
212+
// GetAssetListUpdateTime returns the last time the asset list was updated
213+
func GetAssetListUpdateTime() time.Time {
214+
return listUpdateTime
215+
}
216+
217+
// SetAssetListUpdateTime sets the last time the asset list was updated
218+
func SetAssetListUpdateTime(t time.Time) {
219+
listUpdateTime = t
220+
}

assets/assetProp.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,84 @@ type AssetProp struct {
4343
// Validate is a function called when validating property format.
4444
Validate func(interface{}) error `json:"-"`
4545
}
46+
47+
// ToMap converts an AssetProp to a map[string]interface{}
48+
func (p AssetProp) ToMap() map[string]interface{} {
49+
return map[string]interface{}{
50+
"tag": p.Tag,
51+
"label": p.Label,
52+
"description": p.Description,
53+
"isKey": p.IsKey,
54+
"required": p.Required,
55+
"readOnly": p.ReadOnly,
56+
"defaultValue": p.DefaultValue,
57+
"dataType": p.DataType,
58+
"writers": p.Writers,
59+
}
60+
}
61+
62+
// AssetPropFromMap converts a map[string]interface{} to an AssetProp
63+
func AssetPropFromMap(m map[string]interface{}) AssetProp {
64+
description, ok := m["description"].(string)
65+
if !ok {
66+
description = ""
67+
}
68+
label, ok := m["label"].(string)
69+
if !ok {
70+
label = ""
71+
}
72+
isKey, ok := m["isKey"].(bool)
73+
if !ok {
74+
isKey = false
75+
}
76+
required, ok := m["required"].(bool)
77+
if !ok {
78+
required = false
79+
}
80+
readOnly, ok := m["readOnly"].(bool)
81+
if !ok {
82+
readOnly = false
83+
}
84+
85+
res := AssetProp{
86+
Tag: m["tag"].(string),
87+
Label: label,
88+
Description: description,
89+
IsKey: isKey,
90+
Required: required,
91+
ReadOnly: readOnly,
92+
DefaultValue: m["defaultValue"],
93+
DataType: m["dataType"].(string),
94+
}
95+
96+
writers := make([]string, 0)
97+
writersArr, ok := m["writers"].([]interface{})
98+
if ok {
99+
for _, w := range writersArr {
100+
writers = append(writers, w.(string))
101+
}
102+
}
103+
if len(writers) > 0 {
104+
res.Writers = writers
105+
}
106+
107+
return res
108+
}
109+
110+
// ArrayFromAssetPropList converts an array of AssetProp to an array of map[string]interface
111+
func ArrayFromAssetPropList(a []AssetProp) []map[string]interface{} {
112+
list := []map[string]interface{}{}
113+
for _, m := range a {
114+
list = append(list, m.ToMap())
115+
}
116+
return list
117+
}
118+
119+
// AssetPropListFromArray converts an array of map[string]interface to an array of AssetProp
120+
func AssetPropListFromArray(a []interface{}) []AssetProp {
121+
list := []AssetProp{}
122+
for _, m := range a {
123+
list = append(list, AssetPropFromMap(m.(map[string]interface{})))
124+
}
125+
return list
126+
}

assets/assetType.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ type AssetType struct {
2727

2828
// Validate is a function called when validating asset as a whole.
2929
Validate func(Asset) error `json:"-"`
30+
31+
// Dynamic is a flag that indicates if the asset type is dynamic.
32+
Dynamic bool `json:"dynamic,omitempty"`
3033
}
3134

3235
// Keys returns a list of asset properties which are defined as primary keys. (IsKey == true)
@@ -77,3 +80,68 @@ func (t AssetType) GetPropDef(propTag string) *AssetProp {
7780
func (t AssetType) IsPrivate() bool {
7881
return len(t.Readers) > 0
7982
}
83+
84+
// ToMap returns a map representation of the asset type.
85+
func (t AssetType) ToMap() map[string]interface{} {
86+
return map[string]interface{}{
87+
"tag": t.Tag,
88+
"label": t.Label,
89+
"description": t.Description,
90+
"props": ArrayFromAssetPropList(t.Props),
91+
"readers": t.Readers,
92+
"dynamic": t.Dynamic,
93+
}
94+
}
95+
96+
// AssetTypeFromMap returns an asset type from a map representation.
97+
func AssetTypeFromMap(m map[string]interface{}) AssetType {
98+
label, ok := m["label"].(string)
99+
if !ok {
100+
label = ""
101+
}
102+
description, ok := m["description"].(string)
103+
if !ok {
104+
description = ""
105+
}
106+
dynamic, ok := m["dynamic"].(bool)
107+
if !ok {
108+
dynamic = false
109+
}
110+
111+
res := AssetType{
112+
Tag: m["tag"].(string),
113+
Label: label,
114+
Description: description,
115+
Props: AssetPropListFromArray(m["props"].([]interface{})),
116+
Dynamic: dynamic,
117+
}
118+
119+
readers := make([]string, 0)
120+
readersArr, ok := m["readers"].([]interface{})
121+
if ok {
122+
for _, r := range readersArr {
123+
readers = append(readers, r.(string))
124+
}
125+
}
126+
if len(readers) > 0 {
127+
res.Readers = readers
128+
}
129+
130+
return res
131+
}
132+
133+
// ArrayFromAssetTypeList converts an array of AssetType to an array of map[string]interface
134+
func ArrayFromAssetTypeList(assetTypes []AssetType) (array []map[string]interface{}) {
135+
for _, assetType := range assetTypes {
136+
array = append(array, assetType.ToMap())
137+
}
138+
return
139+
}
140+
141+
// AssetTypeListFromArray converts an array of map[string]interface to an array of AssetType
142+
func AssetTypeListFromArray(array []interface{}) (assetTypes []AssetType) {
143+
for _, v := range array {
144+
assetTypes = append(assetTypes, AssetTypeFromMap(v.(map[string]interface{})))
145+
}
146+
return
147+
}

assets/dynamicAssetType.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package assets
2+
3+
// DynamicAssetType is the configuration for the Dynamic AssetTypes
4+
type DynamicAssetType struct {
5+
// Enabled defines whether the Dynamic AssetTypes feature is active
6+
Enabled bool `json:"enabled"`
7+
8+
// AssetAdmins is an array that specifies which organizations can operate the Dynamic AssetTyper feature.
9+
// Accepts either basic strings for exact matches
10+
// eg. []string{'org1MSP', 'org2MSP'}
11+
// or regular expressions
12+
// eg. []string{`$org\dMSP`} and cc-tools will
13+
// check for a match with regular expression `org\dMSP`
14+
AssetAdmins []string `json:"assetAdmins"`
15+
}

0 commit comments

Comments
 (0)