OSDN Git Service

fix asset alias (#1456)
[bytom/bytom.git] / asset / asset.go
index cd370b8..54096ad 100644 (file)
@@ -2,7 +2,6 @@ package asset
 
 import (
        "context"
-       "encoding/binary"
        "encoding/json"
        "strings"
        "sync"
@@ -12,6 +11,7 @@ import (
        "golang.org/x/crypto/sha3"
 
        "github.com/bytom/blockchain/signers"
+       "github.com/bytom/common"
        "github.com/bytom/consensus"
        "github.com/bytom/crypto/ed25519"
        "github.com/bytom/crypto/ed25519/chainkd"
@@ -27,12 +27,13 @@ var DefaultNativeAsset *Asset
 
 const (
        maxAssetCache = 1000
-       assetPrefix   = "ASS:"
-       //AliasPrefix is asset alias prefix
-       AliasPrefix = "ALS:"
-       //ExternalAssetPrefix is external definition assets prefix
-       ExternalAssetPrefix = "EXA"
-       indexPrefix         = "ASSIDX:"
+)
+
+var (
+       assetIndexKey  = []byte("AssetIndex")
+       assetPrefix    = []byte("Asset:")
+       aliasPrefix    = []byte("AssetAlias:")
+       extAssetPrefix = []byte("EXA:")
 )
 
 func initNativeAsset() {
@@ -51,24 +52,18 @@ func initNativeAsset() {
 }
 
 // AliasKey store asset alias prefix
-func AliasKey(name string) []byte {
-       return []byte(AliasPrefix + name)
+func aliasKey(name string) []byte {
+       return append(aliasPrefix, []byte(name)...)
 }
 
-//Key asset store prefix
+// Key store asset prefix
 func Key(id *bc.AssetID) []byte {
-       name := id.String()
-       return []byte(assetPrefix + name)
-}
-
-func indexKey(xpub chainkd.XPub) []byte {
-       return []byte(indexPrefix + xpub.String())
+       return append(assetPrefix, id.Bytes()...)
 }
 
-//CalcExtAssetKey return store external assets key
-func CalcExtAssetKey(id *bc.AssetID) []byte {
-       name := id.String()
-       return []byte(ExternalAssetPrefix + name)
+// ExtAssetKey return store external assets key
+func ExtAssetKey(id *bc.AssetID) []byte {
+       return append(extAssetPrefix, id.Bytes()...)
 }
 
 // pre-define errors for supporting bytom errorFormatter
@@ -103,6 +98,7 @@ type Registry struct {
        aliasCache *lru.Cache
 
        assetIndexMu sync.Mutex
+       assetMu      sync.Mutex
 }
 
 //Asset describe asset on bytom chain
@@ -116,46 +112,31 @@ type Asset struct {
        DefinitionMap     map[string]interface{} `json:"definition"`
 }
 
-func (reg *Registry) getNextAssetIndex(xpubs []chainkd.XPub) (*uint64, error) {
+func (reg *Registry) getNextAssetIndex() uint64 {
        reg.assetIndexMu.Lock()
        defer reg.assetIndexMu.Unlock()
 
-       var nextIndex uint64 = 1
-
-       if rawIndex := reg.db.Get(indexKey(xpubs[0])); rawIndex != nil {
-               nextIndex = binary.LittleEndian.Uint64(rawIndex) + 1
+       nextIndex := uint64(1)
+       if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
+               nextIndex = common.BytesToUnit64(rawIndex) + 1
        }
 
-       buf := make([]byte, 8)
-       binary.LittleEndian.PutUint64(buf, nextIndex)
-       reg.db.Set(indexKey(xpubs[0]), buf)
-
-       return &nextIndex, nil
+       reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
+       return nextIndex
 }
 
 // Define defines a new Asset.
-func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string) (*Asset, error) {
-       if len(xpubs) == 0 {
-               return nil, errors.Wrap(signers.ErrNoXPubs)
-       }
+func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string, issuanceProgram chainjson.HexBytes) (*Asset, error) {
+       var err error
+       var assetSigner *signers.Signer
 
-       normalizedAlias := strings.ToUpper(strings.TrimSpace(alias))
-       if normalizedAlias == consensus.BTMAlias {
-               return nil, ErrInternalAsset
-       }
-
-       if existed := reg.db.Get(AliasKey(normalizedAlias)); existed != nil {
-               return nil, ErrDuplicateAlias
+       alias = strings.ToUpper(strings.TrimSpace(alias))
+       if alias == "" {
+               return nil, errors.Wrap(ErrNullAlias)
        }
 
-       nextAssetIndex, err := reg.getNextAssetIndex(xpubs)
-       if err != nil {
-               return nil, errors.Wrap(err, "get asset index error")
-       }
-
-       assetSigner, err := signers.Create("asset", xpubs, quorum, *nextAssetIndex)
-       if err != nil {
-               return nil, err
+       if alias == consensus.BTMAlias {
+               return nil, ErrInternalAsset
        }
 
        rawDefinition, err := serializeAssetDef(definition)
@@ -163,43 +144,65 @@ func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[str
                return nil, ErrSerializing
        }
 
-       path := signers.Path(assetSigner, signers.AssetKeySpace)
-       derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
-       derivedPKs := chainkd.XPubKeys(derivedXPubs)
-       issuanceProgram, vmver, err := multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
-       if err != nil {
-               return nil, err
+       vmver := uint64(1)
+       if len(issuanceProgram) == 0 {
+               if len(xpubs) == 0 {
+                       return nil, errors.Wrap(signers.ErrNoXPubs)
+               }
+
+               nextAssetIndex := reg.getNextAssetIndex()
+               assetSigner, err = signers.Create("asset", xpubs, quorum, nextAssetIndex, signers.BIP0032)
+               if err != nil {
+                       return nil, err
+               }
+
+               path := signers.GetBip0032Path(assetSigner, signers.AssetKeySpace)
+               derivedXPubs := chainkd.DeriveXPubs(assetSigner.XPubs, path)
+               derivedPKs := chainkd.XPubKeys(derivedXPubs)
+               issuanceProgram, vmver, err = multisigIssuanceProgram(derivedPKs, assetSigner.Quorum)
+               if err != nil {
+                       return nil, err
+               }
        }
 
        defHash := bc.NewHash(sha3.Sum256(rawDefinition))
-       asset := &Asset{
+       a := &Asset{
                DefinitionMap:     definition,
                RawDefinitionByte: rawDefinition,
                VMVersion:         vmver,
                IssuanceProgram:   issuanceProgram,
                AssetID:           bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
                Signer:            assetSigner,
+               Alias:             &alias,
        }
+       return a, reg.SaveAsset(a, alias)
+}
+
+// SaveAsset store asset
+func (reg *Registry) SaveAsset(a *Asset, alias string) error {
+       reg.assetMu.Lock()
+       defer reg.assetMu.Unlock()
 
-       if existAsset := reg.db.Get(Key(&asset.AssetID)); existAsset != nil {
-               return nil, ErrDuplicateAsset
+       aliasKey := aliasKey(alias)
+       if existed := reg.db.Get(aliasKey); existed != nil {
+               return ErrDuplicateAlias
        }
 
-       if alias != "" {
-               asset.Alias = &normalizedAlias
+       assetKey := Key(&a.AssetID)
+       if existAsset := reg.db.Get(assetKey); existAsset != nil {
+               return ErrDuplicateAsset
        }
 
-       ass, err := json.Marshal(asset)
+       rawAsset, err := json.Marshal(a)
        if err != nil {
-               return nil, ErrMarshalAsset
+               return ErrMarshalAsset
        }
 
        storeBatch := reg.db.NewBatch()
-       storeBatch.Set(AliasKey(normalizedAlias), []byte(asset.AssetID.String()))
-       storeBatch.Set(Key(&asset.AssetID), ass)
+       storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
+       storeBatch.Set(assetKey, rawAsset)
        storeBatch.Write()
-
-       return asset, nil
+       return nil
 }
 
 // FindByID retrieves an Asset record along with its signer, given an assetID.
@@ -227,26 +230,17 @@ func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, erro
        return asset, nil
 }
 
-//GetIDByAlias return AssetID string and nil by asset alias,if err ,return "" and err
-func (reg *Registry) GetIDByAlias(alias string) (string, error) {
-       rawID := reg.db.Get(AliasKey(alias))
-       if rawID == nil {
-               return "", ErrFindAsset
-       }
-       return string(rawID), nil
-}
-
 // FindByAlias retrieves an Asset record along with its signer,
 // given an asset alias.
-func (reg *Registry) FindByAlias(ctx context.Context, alias string) (*Asset, error) {
+func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
        reg.cacheMu.Lock()
        cachedID, ok := reg.aliasCache.Get(alias)
        reg.cacheMu.Unlock()
        if ok {
-               return reg.FindByID(ctx, cachedID.(*bc.AssetID))
+               return reg.FindByID(nil, cachedID.(*bc.AssetID))
        }
 
-       rawID := reg.db.Get(AliasKey(alias))
+       rawID := reg.db.Get(aliasKey(alias))
        if rawID == nil {
                return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
        }
@@ -259,7 +253,7 @@ func (reg *Registry) FindByAlias(ctx context.Context, alias string) (*Asset, err
        reg.cacheMu.Lock()
        reg.aliasCache.Add(alias, assetID)
        reg.cacheMu.Unlock()
-       return reg.FindByID(ctx, assetID)
+       return reg.FindByID(nil, assetID)
 }
 
 //GetAliasByID return asset alias string by AssetID string
@@ -284,31 +278,66 @@ func (reg *Registry) GetAliasByID(id string) string {
 
 // GetAsset get asset by assetID
 func (reg *Registry) GetAsset(id string) (*Asset, error) {
-       asset := &Asset{}
+       var assetID bc.AssetID
+       if err := assetID.UnmarshalText([]byte(id)); err != nil {
+               return nil, err
+       }
 
-       if strings.Compare(id, DefaultNativeAsset.AssetID.String()) == 0 {
+       if assetID.String() == DefaultNativeAsset.AssetID.String() {
                return DefaultNativeAsset, nil
        }
 
-       if interAsset := reg.db.Get([]byte(assetPrefix + id)); interAsset != nil {
+       asset := &Asset{}
+       if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
                if err := json.Unmarshal(interAsset, asset); err != nil {
                        return nil, err
                }
                return asset, nil
        }
 
-       if extAsset := reg.db.Get([]byte(ExternalAssetPrefix + id)); extAsset != nil {
-               if err := json.Unmarshal(extAsset, asset); err != nil {
+       if extAsset := reg.db.Get(ExtAssetKey(&assetID)); extAsset != nil {
+               definitionMap := make(map[string]interface{})
+               if err := json.Unmarshal(extAsset, &definitionMap); err != nil {
                        return nil, err
                }
+               alias := assetID.String()
+               asset.Alias = &alias
+               asset.AssetID = assetID
+               asset.DefinitionMap = definitionMap
+               return asset, nil
        }
-       return asset, nil
+
+       return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
 }
 
 // ListAssets returns the accounts in the db
-func (reg *Registry) ListAssets() ([]*Asset, error) {
+func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
        assets := []*Asset{DefaultNativeAsset}
-       assetIter := reg.db.IteratorPrefix([]byte(assetPrefix))
+
+       assetIDStr := strings.TrimSpace(id)
+       if assetIDStr == DefaultNativeAsset.AssetID.String() {
+               return assets, nil
+       }
+
+       if assetIDStr != "" {
+               assetID := &bc.AssetID{}
+               if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
+                       return nil, err
+               }
+
+               asset := &Asset{}
+               interAsset := reg.db.Get(Key(assetID))
+               if interAsset != nil {
+                       if err := json.Unmarshal(interAsset, asset); err != nil {
+                               return nil, err
+                       }
+                       return []*Asset{asset}, nil
+               }
+
+               return []*Asset{}, nil
+       }
+
+       assetIter := reg.db.IteratorPrefix(assetPrefix)
        defer assetIter.Release()
 
        for assetIter.Next() {
@@ -348,27 +377,30 @@ func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int) (progra
 //UpdateAssetAlias updates asset alias
 func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
        oldAlias := reg.GetAliasByID(id)
-       normalizedAlias := strings.ToUpper(strings.TrimSpace(newAlias))
+       newAlias = strings.ToUpper(strings.TrimSpace(newAlias))
 
        if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
                return ErrInternalAsset
        }
 
-       if oldAlias == "" || normalizedAlias == "" {
+       if oldAlias == "" || newAlias == "" {
                return ErrNullAlias
        }
 
-       if _, err := reg.GetIDByAlias(normalizedAlias); err == nil {
+       reg.assetMu.Lock()
+       defer reg.assetMu.Unlock()
+
+       if _, err := reg.FindByAlias(newAlias); err == nil {
                return ErrDuplicateAlias
        }
 
-       findAsset, err := reg.FindByAlias(nil, oldAlias)
+       findAsset, err := reg.FindByAlias(oldAlias)
        if err != nil {
                return err
        }
 
        storeBatch := reg.db.NewBatch()
-       findAsset.Alias = &normalizedAlias
+       findAsset.Alias = &newAlias
        assetID := &findAsset.AssetID
        rawAsset, err := json.Marshal(findAsset)
        if err != nil {
@@ -376,8 +408,8 @@ func (reg *Registry) UpdateAssetAlias(id, newAlias string) error {
        }
 
        storeBatch.Set(Key(assetID), rawAsset)
-       storeBatch.Set(AliasKey(newAlias), []byte(assetID.String()))
-       storeBatch.Delete(AliasKey(oldAlias))
+       storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
+       storeBatch.Delete(aliasKey(oldAlias))
        storeBatch.Write()
 
        reg.cacheMu.Lock()