9 "github.com/golang/groupcache/lru"
10 dbm "github.com/tendermint/tmlibs/db"
11 "golang.org/x/crypto/sha3"
13 "github.com/bytom/blockchain/signers"
14 "github.com/bytom/common"
15 "github.com/bytom/consensus"
16 "github.com/bytom/crypto/ed25519"
17 "github.com/bytom/crypto/ed25519/chainkd"
18 chainjson "github.com/bytom/encoding/json"
19 "github.com/bytom/errors"
20 "github.com/bytom/protocol"
21 "github.com/bytom/protocol/bc"
22 "github.com/bytom/protocol/vm/vmutil"
25 // DefaultNativeAsset native BTM asset
26 var DefaultNativeAsset *Asset
30 //AliasPrefix is asset alias prefix
32 //ExternalAssetPrefix is external definition assets prefix
33 ExternalAssetPrefix = "EXA"
37 assetIndexKey = []byte("assetIndex")
38 assetPrefix = []byte("ASS:")
41 func initNativeAsset() {
42 signer := &signers.Signer{Type: "internal"}
43 alias := consensus.BTMAlias
45 definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
46 DefaultNativeAsset = &Asset{
48 AssetID: *consensus.BTMAssetID,
51 DefinitionMap: consensus.BTMDefinitionMap,
52 RawDefinitionByte: definitionBytes,
56 // AliasKey store asset alias prefix
57 func AliasKey(name string) []byte {
58 return []byte(AliasPrefix + name)
61 //Key asset store prefix
62 func Key(id *bc.AssetID) []byte {
63 return append(assetPrefix, id.Bytes()...)
66 //CalcExtAssetKey return store external assets key
67 func CalcExtAssetKey(id *bc.AssetID) []byte {
69 return []byte(ExternalAssetPrefix + name)
72 // pre-define errors for supporting bytom errorFormatter
74 ErrDuplicateAlias = errors.New("duplicate asset alias")
75 ErrDuplicateAsset = errors.New("duplicate asset id")
76 ErrSerializing = errors.New("serializing asset definition")
77 ErrMarshalAsset = errors.New("failed marshal asset")
78 ErrFindAsset = errors.New("fail to find asset")
79 ErrInternalAsset = errors.New("btm has been defined as the internal asset")
80 ErrNullAlias = errors.New("null asset alias")
83 //NewRegistry create new registry
84 func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
89 cache: lru.New(maxAssetCache),
90 aliasCache: lru.New(maxAssetCache),
94 // Registry tracks and stores all known assets on a blockchain.
95 type Registry struct {
101 aliasCache *lru.Cache
103 assetIndexMu sync.Mutex
106 //Asset describe asset on bytom chain
109 AssetID bc.AssetID `json:"id"`
110 Alias *string `json:"alias"`
111 VMVersion uint64 `json:"vm_version"`
112 IssuanceProgram chainjson.HexBytes `json:"issue_program"`
113 RawDefinitionByte chainjson.HexBytes `json:"raw_definition_byte"`
114 DefinitionMap map[string]interface{} `json:"definition"`
117 func (reg *Registry) getNextAssetIndex() uint64 {
118 reg.assetIndexMu.Lock()
119 defer reg.assetIndexMu.Unlock()
121 nextIndex := uint64(1)
122 if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
123 nextIndex = common.BytesToUnit64(rawIndex) + 1
126 reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
130 // Define defines a new Asset.
131 func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string) (*Asset, error) {
133 return nil, errors.Wrap(signers.ErrNoXPubs)
136 normalizedAlias := strings.ToUpper(strings.TrimSpace(alias))
137 if normalizedAlias == consensus.BTMAlias {
138 return nil, ErrInternalAsset
141 if existed := reg.db.Get(AliasKey(normalizedAlias)); existed != nil {
142 return nil, ErrDuplicateAlias
145 nextAssetIndex := reg.getNextAssetIndex()
146 assetSigner, err := signers.Create("asset", xpubs, quorum, nextAssetIndex)
151 rawDefinition, err := serializeAssetDef(definition)
153 return nil, ErrSerializing
156 path := signers.Path(assetSigner, signers.AssetKeySpace)
157 derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
158 derivedPKs := chainkd.XPubKeys(derivedXPubs)
159 issuanceProgram, vmver, err := multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
164 defHash := bc.NewHash(sha3.Sum256(rawDefinition))
166 DefinitionMap: definition,
167 RawDefinitionByte: rawDefinition,
169 IssuanceProgram: issuanceProgram,
170 AssetID: bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
174 if existAsset := reg.db.Get(Key(&asset.AssetID)); existAsset != nil {
175 return nil, ErrDuplicateAsset
179 asset.Alias = &normalizedAlias
182 ass, err := json.Marshal(asset)
184 return nil, ErrMarshalAsset
187 storeBatch := reg.db.NewBatch()
188 storeBatch.Set(AliasKey(normalizedAlias), []byte(asset.AssetID.String()))
189 storeBatch.Set(Key(&asset.AssetID), ass)
195 // FindByID retrieves an Asset record along with its signer, given an assetID.
196 func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
198 cached, ok := reg.cache.Get(id.String())
201 return cached.(*Asset), nil
204 bytes := reg.db.Get(Key(id))
206 return nil, ErrFindAsset
210 if err := json.Unmarshal(bytes, asset); err != nil {
215 reg.cache.Add(id.String(), asset)
220 //GetIDByAlias return AssetID string and nil by asset alias,if err ,return "" and err
221 func (reg *Registry) GetIDByAlias(alias string) (string, error) {
222 rawID := reg.db.Get(AliasKey(alias))
224 return "", ErrFindAsset
226 return string(rawID), nil
229 // FindByAlias retrieves an Asset record along with its signer,
230 // given an asset alias.
231 func (reg *Registry) FindByAlias(ctx context.Context, alias string) (*Asset, error) {
233 cachedID, ok := reg.aliasCache.Get(alias)
236 return reg.FindByID(ctx, cachedID.(*bc.AssetID))
239 rawID := reg.db.Get(AliasKey(alias))
241 return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
244 assetID := &bc.AssetID{}
245 if err := assetID.UnmarshalText(rawID); err != nil {
250 reg.aliasCache.Add(alias, assetID)
252 return reg.FindByID(ctx, assetID)
255 //GetAliasByID return asset alias string by AssetID string
256 func (reg *Registry) GetAliasByID(id string) string {
258 if id == consensus.BTMAssetID.String() {
259 return consensus.BTMAlias
262 assetID := &bc.AssetID{}
263 if err := assetID.UnmarshalText([]byte(id)); err != nil {
267 asset, err := reg.FindByID(nil, assetID)
275 // GetAsset get asset by assetID
276 func (reg *Registry) GetAsset(id string) (*Asset, error) {
279 if strings.Compare(id, DefaultNativeAsset.AssetID.String()) == 0 {
280 return DefaultNativeAsset, nil
283 if interAsset := reg.db.Get([]byte(assetPrefix + id)); interAsset != nil {
284 if err := json.Unmarshal(interAsset, asset); err != nil {
290 if extAsset := reg.db.Get([]byte(ExternalAssetPrefix + id)); extAsset != nil {
291 if err := json.Unmarshal(extAsset, asset); err != nil {
298 // ListAssets returns the accounts in the db
299 func (reg *Registry) ListAssets() ([]*Asset, error) {
300 assets := []*Asset{DefaultNativeAsset}
301 assetIter := reg.db.IteratorPrefix(assetPrefix)
302 defer assetIter.Release()
304 for assetIter.Next() {
306 if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
309 assets = append(assets, asset)
315 // serializeAssetDef produces a canonical byte representation of an asset
316 // definition. Currently, this is implemented using pretty-printed JSON.
317 // As is the standard for Go's map[string] serialization, object keys will
318 // appear in lexicographic order. Although this is mostly meant for machine
319 // consumption, the JSON is pretty-printed for easy reading.
320 func serializeAssetDef(def map[string]interface{}) ([]byte, error) {
322 def = make(map[string]interface{}, 0)
324 return json.MarshalIndent(def, "", " ")
327 func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int) (program []byte, vmversion uint64, err error) {
328 issuanceProg, err := vmutil.P2SPMultiSigProgram(pubkeys, nrequired)
332 builder := vmutil.NewBuilder()
333 builder.AddRawBytes(issuanceProg)
334 prog, err := builder.Build()
338 //UpdateAssetAlias updates asset alias
339 func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
340 oldAlias := reg.GetAliasByID(id)
341 normalizedAlias := strings.ToUpper(strings.TrimSpace(newAlias))
343 if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
344 return ErrInternalAsset
347 if oldAlias == "" || normalizedAlias == "" {
351 if _, err := reg.GetIDByAlias(normalizedAlias); err == nil {
352 return ErrDuplicateAlias
355 findAsset, err := reg.FindByAlias(nil, oldAlias)
360 storeBatch := reg.db.NewBatch()
361 findAsset.Alias = &normalizedAlias
362 assetID := &findAsset.AssetID
363 rawAsset, err := json.Marshal(findAsset)
368 storeBatch.Set(Key(assetID), rawAsset)
369 storeBatch.Set(AliasKey(newAlias), []byte(assetID.String()))
370 storeBatch.Delete(AliasKey(oldAlias))
374 reg.aliasCache.Add(newAlias, assetID)
375 reg.aliasCache.Remove(oldAlias)