9 "github.com/golang/groupcache/lru"
11 "github.com/vapor/consensus"
12 dbm "github.com/vapor/database/leveldb"
13 chainjson "github.com/vapor/encoding/json"
14 "github.com/vapor/errors"
15 "github.com/vapor/protocol"
16 "github.com/vapor/protocol/bc"
19 // DefaultNativeAsset native BTM asset
20 var DefaultNativeAsset *Asset
27 assetIndexKey = []byte("AssetIndex")
28 assetPrefix = []byte("Asset:")
29 aliasPrefix = []byte("AssetAlias:")
30 extAssetPrefix = []byte("EXA:")
33 func initNativeAsset() {
34 alias := consensus.BTMAlias
36 definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
37 DefaultNativeAsset = &Asset{
38 AssetID: *consensus.BTMAssetID,
41 DefinitionMap: consensus.BTMDefinitionMap,
42 RawDefinitionByte: definitionBytes,
46 // AliasKey store asset alias prefix
47 func aliasKey(name string) []byte {
48 return append(aliasPrefix, []byte(name)...)
51 // Key store asset prefix
52 func Key(id *bc.AssetID) []byte {
53 return append(assetPrefix, id.Bytes()...)
56 // ExtAssetKey return store external assets key
57 func ExtAssetKey(id *bc.AssetID) []byte {
58 return append(extAssetPrefix, id.Bytes()...)
61 // pre-define errors for supporting bytom errorFormatter
63 ErrDuplicateAlias = errors.New("duplicate asset alias")
64 ErrDuplicateAsset = errors.New("duplicate asset id")
65 ErrSerializing = errors.New("serializing asset definition")
66 ErrMarshalAsset = errors.New("failed marshal asset")
67 ErrFindAsset = errors.New("fail to find asset")
68 ErrInternalAsset = errors.New("btm has been defined as the internal asset")
69 ErrNullAlias = errors.New("null asset alias")
72 //NewRegistry create new registry
73 func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
78 cache: lru.New(maxAssetCache),
79 aliasCache: lru.New(maxAssetCache),
83 // Registry tracks and stores all known assets on a blockchain.
84 type Registry struct {
92 assetIndexMu sync.Mutex
96 //Asset describe asset on bytom chain
98 AssetID bc.AssetID `json:"id"`
99 Alias *string `json:"alias"`
100 VMVersion uint64 `json:"vm_version"`
101 RawDefinitionByte chainjson.HexBytes `json:"raw_definition_byte"`
102 DefinitionMap map[string]interface{} `json:"definition"`
105 // SaveAsset store asset
106 func (reg *Registry) SaveAsset(a *Asset, alias string) error {
108 defer reg.assetMu.Unlock()
110 aliasKey := aliasKey(alias)
111 if existed := reg.db.Get(aliasKey); existed != nil {
112 return ErrDuplicateAlias
115 assetKey := Key(&a.AssetID)
116 if existAsset := reg.db.Get(assetKey); existAsset != nil {
117 return ErrDuplicateAsset
120 rawAsset, err := json.Marshal(a)
122 return ErrMarshalAsset
125 storeBatch := reg.db.NewBatch()
126 storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
127 storeBatch.Set(assetKey, rawAsset)
132 // FindByID retrieves an Asset record along with its signer, given an assetID.
133 func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
135 cached, ok := reg.cache.Get(id.String())
138 return cached.(*Asset), nil
141 bytes := reg.db.Get(Key(id))
143 return nil, ErrFindAsset
147 if err := json.Unmarshal(bytes, asset); err != nil {
152 reg.cache.Add(id.String(), asset)
157 // FindByAlias retrieves an Asset record along with its signer,
158 // given an asset alias.
159 func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
161 cachedID, ok := reg.aliasCache.Get(alias)
164 return reg.FindByID(nil, cachedID.(*bc.AssetID))
167 rawID := reg.db.Get(aliasKey(alias))
169 return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
172 assetID := &bc.AssetID{}
173 if err := assetID.UnmarshalText(rawID); err != nil {
178 reg.aliasCache.Add(alias, assetID)
180 return reg.FindByID(nil, assetID)
183 //GetAliasByID return asset alias string by AssetID string
184 func (reg *Registry) GetAliasByID(id string) string {
186 if id == consensus.BTMAssetID.String() {
187 return consensus.BTMAlias
190 assetID := &bc.AssetID{}
191 if err := assetID.UnmarshalText([]byte(id)); err != nil {
195 asset, err := reg.FindByID(nil, assetID)
203 // GetAsset get asset by assetID
204 func (reg *Registry) GetAsset(id string) (*Asset, error) {
205 var assetID bc.AssetID
206 if err := assetID.UnmarshalText([]byte(id)); err != nil {
210 if assetID.String() == DefaultNativeAsset.AssetID.String() {
211 return DefaultNativeAsset, nil
215 if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
216 if err := json.Unmarshal(interAsset, asset); err != nil {
222 if extAsset := reg.db.Get(ExtAssetKey(&assetID)); extAsset != nil {
223 definitionMap := make(map[string]interface{})
224 if err := json.Unmarshal(extAsset, &definitionMap); err != nil {
227 alias := assetID.String()
229 asset.AssetID = assetID
230 asset.DefinitionMap = definitionMap
234 return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
237 // ListAssets returns the accounts in the db
238 func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
239 assets := []*Asset{DefaultNativeAsset}
241 assetIDStr := strings.TrimSpace(id)
242 if assetIDStr == DefaultNativeAsset.AssetID.String() {
246 if assetIDStr != "" {
247 assetID := &bc.AssetID{}
248 if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
253 interAsset := reg.db.Get(Key(assetID))
254 if interAsset != nil {
255 if err := json.Unmarshal(interAsset, asset); err != nil {
258 return []*Asset{asset}, nil
261 return []*Asset{}, nil
264 assetIter := reg.db.IteratorPrefix(assetPrefix)
265 defer assetIter.Release()
267 for assetIter.Next() {
269 if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
272 assets = append(assets, asset)
278 // serializeAssetDef produces a canonical byte representation of an asset
279 // definition. Currently, this is implemented using pretty-printed JSON.
280 // As is the standard for Go's map[string] serialization, object keys will
281 // appear in lexicographic order. Although this is mostly meant for machine
282 // consumption, the JSON is pretty-printed for easy reading.
283 func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
285 def = make(map[string]interface{}, 0)
287 return json.MarshalIndent(def, "", " ")
290 //UpdateAssetAlias updates asset alias
291 func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
292 oldAlias := reg.GetAliasByID(id)
293 newAlias = strings.ToUpper(strings.TrimSpace(newAlias))
295 if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
296 return ErrInternalAsset
299 if oldAlias == "" || newAlias == "" {
304 defer reg.assetMu.Unlock()
306 if _, err := reg.FindByAlias(newAlias); err == nil {
307 return ErrDuplicateAlias
310 findAsset, err := reg.FindByAlias(oldAlias)
315 storeBatch := reg.db.NewBatch()
316 findAsset.Alias = &newAlias
317 assetID := &findAsset.AssetID
318 rawAsset, err := json.Marshal(findAsset)
323 storeBatch.Set(Key(assetID), rawAsset)
324 storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
325 storeBatch.Delete(aliasKey(oldAlias))
329 reg.aliasCache.Add(newAlias, assetID)
330 reg.aliasCache.Remove(oldAlias)