import (
"context"
- "encoding/binary"
"encoding/json"
"strings"
"sync"
"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"
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() {
}
// 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
aliasCache *lru.Cache
assetIndexMu sync.Mutex
+ assetMu sync.Mutex
}
//Asset describe asset on bytom chain
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)
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.
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)
}
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
// 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() {
//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 {
}
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()