OSDN Git Service

Merge pull request #37 from Bytom/dev
[bytom/vapor.git] / asset / asset.go
1 package asset
2
3 import (
4         "context"
5         "encoding/json"
6         "strings"
7         "sync"
8
9         "github.com/golang/groupcache/lru"
10         dbm "github.com/tendermint/tmlibs/db"
11         "golang.org/x/crypto/sha3"
12
13         "github.com/vapor/blockchain/signers"
14         "github.com/vapor/common"
15         "github.com/vapor/consensus"
16         "github.com/vapor/crypto/ed25519"
17         "github.com/vapor/crypto/ed25519/chainkd"
18         chainjson "github.com/vapor/encoding/json"
19         "github.com/vapor/errors"
20         "github.com/vapor/protocol"
21         "github.com/vapor/protocol/bc"
22         "github.com/vapor/protocol/vm/vmutil"
23 )
24
25 // DefaultNativeAsset native BTM asset
26 var DefaultNativeAsset *Asset
27
28 const (
29         maxAssetCache = 1000
30 )
31
32 var (
33         assetIndexKey  = []byte("AssetIndex")
34         assetPrefix    = []byte("Asset:")
35         aliasPrefix    = []byte("AssetAlias:")
36         extAssetPrefix = []byte("EXA:")
37 )
38
39 func initNativeAsset() {
40         signer := &signers.Signer{Type: "internal"}
41         alias := consensus.BTMAlias
42
43         definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
44         DefaultNativeAsset = &Asset{
45                 Signer:            signer,
46                 AssetID:           *consensus.BTMAssetID,
47                 Alias:             &alias,
48                 VMVersion:         1,
49                 DefinitionMap:     consensus.BTMDefinitionMap,
50                 RawDefinitionByte: definitionBytes,
51         }
52 }
53
54 // AliasKey store asset alias prefix
55 func aliasKey(name string) []byte {
56         return append(aliasPrefix, []byte(name)...)
57 }
58
59 // Key store asset prefix
60 func Key(id *bc.AssetID) []byte {
61         return append(assetPrefix, id.Bytes()...)
62 }
63
64 // ExtAssetKey return store external assets key
65 func ExtAssetKey(id *bc.AssetID) []byte {
66         return append(extAssetPrefix, id.Bytes()...)
67 }
68
69 // pre-define errors for supporting bytom errorFormatter
70 var (
71         ErrDuplicateAlias = errors.New("duplicate asset alias")
72         ErrDuplicateAsset = errors.New("duplicate asset id")
73         ErrSerializing    = errors.New("serializing asset definition")
74         ErrMarshalAsset   = errors.New("failed marshal asset")
75         ErrFindAsset      = errors.New("fail to find asset")
76         ErrInternalAsset  = errors.New("btm has been defined as the internal asset")
77         ErrNullAlias      = errors.New("null asset alias")
78 )
79
80 //NewRegistry create new registry
81 func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
82         initNativeAsset()
83         return &Registry{
84                 db:         db,
85                 chain:      chain,
86                 cache:      lru.New(maxAssetCache),
87                 aliasCache: lru.New(maxAssetCache),
88         }
89 }
90
91 // Registry tracks and stores all known assets on a blockchain.
92 type Registry struct {
93         db    dbm.DB
94         chain *protocol.Chain
95
96         cacheMu    sync.Mutex
97         cache      *lru.Cache
98         aliasCache *lru.Cache
99
100         assetIndexMu sync.Mutex
101         assetMu      sync.Mutex
102 }
103
104 //Asset describe asset on bytom chain
105 type Asset struct {
106         *signers.Signer
107         AssetID           bc.AssetID             `json:"id"`
108         Alias             *string                `json:"alias"`
109         VMVersion         uint64                 `json:"vm_version"`
110         IssuanceProgram   chainjson.HexBytes     `json:"issue_program"`
111         RawDefinitionByte chainjson.HexBytes     `json:"raw_definition_byte"`
112         DefinitionMap     map[string]interface{} `json:"definition"`
113 }
114
115 func (reg *Registry) getNextAssetIndex() uint64 {
116         reg.assetIndexMu.Lock()
117         defer reg.assetIndexMu.Unlock()
118
119         nextIndex := uint64(1)
120         if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
121                 nextIndex = common.BytesToUnit64(rawIndex) + 1
122         }
123
124         reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
125         return nextIndex
126 }
127
128 // Define defines a new Asset.
129 func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string, issuanceProgram chainjson.HexBytes) (*Asset, error) {
130         var err error
131         var assetSigner *signers.Signer
132
133         alias = strings.ToUpper(strings.TrimSpace(alias))
134         if alias == "" {
135                 return nil, errors.Wrap(ErrNullAlias)
136         }
137
138         if alias == consensus.BTMAlias {
139                 return nil, ErrInternalAsset
140         }
141
142         rawDefinition, err := serializeAssetDef(definition)
143         if err != nil {
144                 return nil, ErrSerializing
145         }
146
147         vmver := uint64(1)
148         if len(issuanceProgram) == 0 {
149                 if len(xpubs) == 0 {
150                         return nil, errors.Wrap(signers.ErrNoXPubs)
151                 }
152
153                 nextAssetIndex := reg.getNextAssetIndex()
154                 assetSigner, err = signers.Create("asset", xpubs, quorum, nextAssetIndex, signers.BIP0032)
155                 if err != nil {
156                         return nil, err
157                 }
158
159                 path := signers.GetBip0032Path(assetSigner, signers.AssetKeySpace)
160                 derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
161                 derivedPKs := chainkd.XPubKeys(derivedXPubs)
162                 issuanceProgram, vmver, err = multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
163                 if err != nil {
164                         return nil, err
165                 }
166         }
167
168         defHash := bc.NewHash(sha3.Sum256(rawDefinition))
169         a := &Asset{
170                 DefinitionMap:     definition,
171                 RawDefinitionByte: rawDefinition,
172                 VMVersion:         vmver,
173                 IssuanceProgram:   issuanceProgram,
174                 AssetID:           bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
175                 Signer:            assetSigner,
176                 Alias:             &alias,
177         }
178         return a, reg.SaveAsset(a, alias)
179 }
180
181 // SaveAsset store asset
182 func (reg *Registry) SaveAsset(a *Asset, alias string) error {
183         reg.assetMu.Lock()
184         defer reg.assetMu.Unlock()
185
186         aliasKey := aliasKey(alias)
187         if existed := reg.db.Get(aliasKey); existed != nil {
188                 return ErrDuplicateAlias
189         }
190
191         assetKey := Key(&a.AssetID)
192         if existAsset := reg.db.Get(assetKey); existAsset != nil {
193                 return ErrDuplicateAsset
194         }
195
196         rawAsset, err := json.Marshal(a)
197         if err != nil {
198                 return ErrMarshalAsset
199         }
200
201         storeBatch := reg.db.NewBatch()
202         storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
203         storeBatch.Set(assetKey, rawAsset)
204         storeBatch.Write()
205         return nil
206 }
207
208 // FindByID retrieves an Asset record along with its signer, given an assetID.
209 func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
210         reg.cacheMu.Lock()
211         cached, ok := reg.cache.Get(id.String())
212         reg.cacheMu.Unlock()
213         if ok {
214                 return cached.(*Asset), nil
215         }
216
217         bytes := reg.db.Get(Key(id))
218         if bytes == nil {
219                 return nil, ErrFindAsset
220         }
221
222         asset := &Asset{}
223         if err := json.Unmarshal(bytes, asset); err != nil {
224                 return nil, err
225         }
226
227         reg.cacheMu.Lock()
228         reg.cache.Add(id.String(), asset)
229         reg.cacheMu.Unlock()
230         return asset, nil
231 }
232
233 // FindByAlias retrieves an Asset record along with its signer,
234 // given an asset alias.
235 func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
236         reg.cacheMu.Lock()
237         cachedID, ok := reg.aliasCache.Get(alias)
238         reg.cacheMu.Unlock()
239         if ok {
240                 return reg.FindByID(nil, cachedID.(*bc.AssetID))
241         }
242
243         rawID := reg.db.Get(aliasKey(alias))
244         if rawID == nil {
245                 return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
246         }
247
248         assetID := &bc.AssetID{}
249         if err := assetID.UnmarshalText(rawID); err != nil {
250                 return nil, err
251         }
252
253         reg.cacheMu.Lock()
254         reg.aliasCache.Add(alias, assetID)
255         reg.cacheMu.Unlock()
256         return reg.FindByID(nil, assetID)
257 }
258
259 //GetAliasByID return asset alias string by AssetID string
260 func (reg *Registry) GetAliasByID(id string) string {
261         //btm
262         if id == consensus.BTMAssetID.String() {
263                 return consensus.BTMAlias
264         }
265
266         assetID := &bc.AssetID{}
267         if err := assetID.UnmarshalText([]byte(id)); err != nil {
268                 return ""
269         }
270
271         asset, err := reg.FindByID(nil, assetID)
272         if err != nil {
273                 return ""
274         }
275
276         return *asset.Alias
277 }
278
279 // GetAsset get asset by assetID
280 func (reg *Registry) GetAsset(id string) (*Asset, error) {
281         var assetID bc.AssetID
282         if err := assetID.UnmarshalText([]byte(id)); err != nil {
283                 return nil, err
284         }
285
286         if assetID.String() == DefaultNativeAsset.AssetID.String() {
287                 return DefaultNativeAsset, nil
288         }
289
290         asset := &Asset{}
291         if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
292                 if err := json.Unmarshal(interAsset, asset); err != nil {
293                         return nil, err
294                 }
295                 return asset, nil
296         }
297
298         if extAsset := reg.db.Get(ExtAssetKey(&assetID)); extAsset != nil {
299                 definitionMap := make(map[string]interface{})
300                 if err := json.Unmarshal(extAsset, &definitionMap); err != nil {
301                         return nil, err
302                 }
303                 alias := assetID.String()
304                 asset.Alias = &alias
305                 asset.AssetID = assetID
306                 asset.DefinitionMap = definitionMap
307                 return asset, nil
308         }
309
310         return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
311 }
312
313 // ListAssets returns the accounts in the db
314 func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
315         assets := []*Asset{DefaultNativeAsset}
316
317         assetIDStr := strings.TrimSpace(id)
318         if assetIDStr == DefaultNativeAsset.AssetID.String() {
319                 return assets, nil
320         }
321
322         if assetIDStr != "" {
323                 assetID := &bc.AssetID{}
324                 if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
325                         return nil, err
326                 }
327
328                 asset := &Asset{}
329                 interAsset := reg.db.Get(Key(assetID))
330                 if interAsset != nil {
331                         if err := json.Unmarshal(interAsset, asset); err != nil {
332                                 return nil, err
333                         }
334                         return []*Asset{asset}, nil
335                 }
336
337                 return []*Asset{}, nil
338         }
339
340         assetIter := reg.db.IteratorPrefix(assetPrefix)
341         defer assetIter.Release()
342
343         for assetIter.Next() {
344                 asset := &Asset{}
345                 if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
346                         return nil, err
347                 }
348                 assets = append(assets, asset)
349         }
350
351         return assets, nil
352 }
353
354 // serializeAssetDef produces a canonical byte representation of an asset
355 // definition. Currently, this is implemented using pretty-printed JSON.
356 // As is the standard for Go's map[string] serialization, object keys will
357 // appear in lexicographic order. Although this is mostly meant for machine
358 // consumption, the JSON is pretty-printed for easy reading.
359 func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
360         if def == nil {
361                 def = make(map[string]interface{}, 0)
362         }
363         return json.MarshalIndent(def, "", "  ")
364 }
365
366 func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, vmversion uint64, err error) {
367         issuanceProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
368         if err != nil {
369                 return nil, 0, err
370         }
371         builder := vmutil.NewBuilder()
372         builder.AddRawBytes(issuanceProg)
373         prog, err := builder.Build()
374         return prog, 1, err
375 }
376
377 //UpdateAssetAlias updates asset alias
378 func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
379         oldAlias := reg.GetAliasByID(id)
380         newAlias = strings.ToUpper(strings.TrimSpace(newAlias))
381
382         if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
383                 return ErrInternalAsset
384         }
385
386         if oldAlias == "" || newAlias == "" {
387                 return ErrNullAlias
388         }
389
390         reg.assetMu.Lock()
391         defer reg.assetMu.Unlock()
392
393         if _, err := reg.FindByAlias(newAlias); err == nil {
394                 return ErrDuplicateAlias
395         }
396
397         findAsset, err := reg.FindByAlias(oldAlias)
398         if err != nil {
399                 return err
400         }
401
402         storeBatch := reg.db.NewBatch()
403         findAsset.Alias = &newAlias
404         assetID := &findAsset.AssetID
405         rawAsset, err := json.Marshal(findAsset)
406         if err != nil {
407                 return err
408         }
409
410         storeBatch.Set(Key(assetID), rawAsset)
411         storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
412         storeBatch.Delete(aliasKey(oldAlias))
413         storeBatch.Write()
414
415         reg.cacheMu.Lock()
416         reg.aliasCache.Add(newAlias, assetID)
417         reg.aliasCache.Remove(oldAlias)
418         reg.cacheMu.Unlock()
419
420         return nil
421 }