10 "github.com/golang/groupcache/lru"
11 "golang.org/x/crypto/sha3"
13 "github.com/bytom/bytom/blockchain/signers"
14 "github.com/bytom/bytom/common"
15 "github.com/bytom/bytom/consensus"
16 "github.com/bytom/bytom/crypto/ed25519/chainkd"
17 dbm "github.com/bytom/bytom/database/leveldb"
18 chainjson "github.com/bytom/bytom/encoding/json"
19 "github.com/bytom/bytom/errors"
20 "github.com/bytom/bytom/protocol"
21 "github.com/bytom/bytom/protocol/bc"
22 "github.com/bytom/bytom/protocol/vm/vmutil"
25 // DefaultNativeAsset native BTM asset
26 var DefaultNativeAsset *Asset
33 assetIndexKey = []byte("AssetIndex")
34 assetPrefix = []byte("Asset:")
35 aliasPrefix = []byte("AssetAlias:")
36 extAssetPrefix = []byte("EXA:")
39 func initNativeAsset() {
40 signer := &signers.Signer{Type: "internal"}
41 alias := consensus.BTMAlias
43 definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
44 DefaultNativeAsset = &Asset{
46 AssetID: *consensus.BTMAssetID,
49 DefinitionMap: consensus.BTMDefinitionMap,
50 RawDefinitionByte: definitionBytes,
54 // AliasKey store asset alias prefix
55 func aliasKey(name string) []byte {
56 return append(aliasPrefix, []byte(name)...)
59 // Key store asset prefix
60 func Key(id *bc.AssetID) []byte {
61 return append(assetPrefix, id.Bytes()...)
64 // ExtAssetKey return store external assets key
65 func ExtAssetKey(id *bc.AssetID) []byte {
66 return append(extAssetPrefix, id.Bytes()...)
69 // pre-define errors for supporting bytom errorFormatter
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")
80 //NewRegistry create new registry
81 func NewRegistry(db dbm.DB, chain *protocol.Chain) *Registry {
86 cache: lru.New(maxAssetCache),
87 aliasCache: lru.New(maxAssetCache),
91 // Registry tracks and stores all known assets on a blockchain.
92 type Registry struct {
100 assetIndexMu sync.Mutex
104 //Asset describe asset on bytom chain
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"`
115 func (reg *Registry) getNextAssetIndex() uint64 {
116 reg.assetIndexMu.Lock()
117 defer reg.assetIndexMu.Unlock()
119 nextIndex := uint64(1)
120 if rawIndex := reg.db.Get(assetIndexKey); rawIndex != nil {
121 nextIndex = common.BytesToUnit64(rawIndex) + 1
124 reg.db.Set(assetIndexKey, common.Unit64ToBytes(nextIndex))
128 // Define defines a new Asset.
129 func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, limitHeight uint64, alias string, issuanceProgram chainjson.HexBytes) (*Asset, error) {
131 var assetSigner *signers.Signer
133 alias = strings.ToUpper(strings.TrimSpace(alias))
135 return nil, errors.Wrap(ErrNullAlias)
138 if alias == consensus.BTMAlias {
139 return nil, ErrInternalAsset
142 rawDefinition, err := serializeAssetDef(definition)
144 return nil, ErrSerializing
148 if len(issuanceProgram) == 0 {
150 return nil, errors.Wrap(signers.ErrNoXPubs)
153 nextAssetIndex := reg.getNextAssetIndex()
154 assetSigner, err = signers.Create("asset", xpubs, quorum, nextAssetIndex, signers.BIP0032)
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, limitHeight)
168 defHash := bc.NewHash(sha3.Sum256(rawDefinition))
170 DefinitionMap: definition,
171 RawDefinitionByte: rawDefinition,
173 IssuanceProgram: issuanceProgram,
174 AssetID: bc.ComputeAssetID(issuanceProgram, vmver, &defHash),
178 return a, reg.SaveAsset(a, alias)
181 // SaveAsset store asset
182 func (reg *Registry) SaveAsset(a *Asset, alias string) error {
184 defer reg.assetMu.Unlock()
186 aliasKey := aliasKey(alias)
187 if existed := reg.db.Get(aliasKey); existed != nil {
188 return ErrDuplicateAlias
191 assetKey := Key(&a.AssetID)
192 if existAsset := reg.db.Get(assetKey); existAsset != nil {
193 return ErrDuplicateAsset
196 rawAsset, err := json.Marshal(a)
198 return ErrMarshalAsset
201 storeBatch := reg.db.NewBatch()
202 storeBatch.Set(aliasKey, []byte(a.AssetID.String()))
203 storeBatch.Set(assetKey, rawAsset)
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) {
211 cached, ok := reg.cache.Get(id.String())
214 return cached.(*Asset), nil
217 bytes := reg.db.Get(Key(id))
219 return nil, ErrFindAsset
223 if err := json.Unmarshal(bytes, asset); err != nil {
228 reg.cache.Add(id.String(), asset)
233 // FindByAlias retrieves an Asset record along with its signer,
234 // given an asset alias.
235 func (reg *Registry) FindByAlias(alias string) (*Asset, error) {
237 cachedID, ok := reg.aliasCache.Get(alias)
240 return reg.FindByID(nil, cachedID.(*bc.AssetID))
243 rawID := reg.db.Get(aliasKey(alias))
245 return nil, errors.Wrapf(ErrFindAsset, "no such asset, alias: %s", alias)
248 assetID := &bc.AssetID{}
249 if err := assetID.UnmarshalText(rawID); err != nil {
254 reg.aliasCache.Add(alias, assetID)
256 return reg.FindByID(nil, assetID)
259 //GetAliasByID return asset alias string by AssetID string
260 func (reg *Registry) GetAliasByID(id string) string {
262 if id == consensus.BTMAssetID.String() {
263 return consensus.BTMAlias
266 assetID := &bc.AssetID{}
267 if err := assetID.UnmarshalText([]byte(id)); err != nil {
271 asset, err := reg.FindByID(nil, assetID)
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 {
286 if assetID.String() == DefaultNativeAsset.AssetID.String() {
287 return DefaultNativeAsset, nil
291 if interAsset := reg.db.Get(Key(&assetID)); interAsset != nil {
292 if err := json.Unmarshal(interAsset, asset); err != nil {
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 {
303 alias := assetID.String()
305 asset.AssetID = assetID
306 asset.DefinitionMap = definitionMap
310 return nil, errors.WithDetailf(ErrFindAsset, "no such asset, assetID: %s", id)
313 // ListAssets returns the accounts in the db
314 func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
315 assets := []*Asset{DefaultNativeAsset}
317 assetIDStr := strings.TrimSpace(id)
318 if assetIDStr == DefaultNativeAsset.AssetID.String() {
322 if assetIDStr != "" {
323 assetID := &bc.AssetID{}
324 if err := assetID.UnmarshalText([]byte(assetIDStr)); err != nil {
329 interAsset := reg.db.Get(Key(assetID))
330 if interAsset != nil {
331 if err := json.Unmarshal(interAsset, asset); err != nil {
334 return []*Asset{asset}, nil
337 return []*Asset{}, nil
340 assetIter := reg.db.IteratorPrefix(assetPrefix)
341 defer assetIter.Release()
343 for assetIter.Next() {
345 if err := json.Unmarshal(assetIter.Value(), asset); err != nil {
348 assets = append(assets, asset)
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) {
361 def = make(map[string]interface{}, 0)
363 return json.MarshalIndent(def, "", " ")
366 func multisigIssuanceProgram(pubkeys []ed25519.PublicKey, nrequired int, blockHeight uint64) (program []byte, vmversion uint64, err error) {
367 issuanceProg, err := vmutil.P2SPMultiSigProgramWithHeight(pubkeys, nrequired, blockHeight)
371 builder := vmutil.NewBuilder()
372 builder.AddRawBytes(issuanceProg)
373 prog, err := builder.Build()
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))
382 if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
383 return ErrInternalAsset
386 if oldAlias == "" || newAlias == "" {
391 defer reg.assetMu.Unlock()
393 if _, err := reg.FindByAlias(newAlias); err == nil {
394 return ErrDuplicateAlias
397 findAsset, err := reg.FindByAlias(oldAlias)
402 storeBatch := reg.db.NewBatch()
403 findAsset.Alias = &newAlias
404 assetID := &findAsset.AssetID
405 rawAsset, err := json.Marshal(findAsset)
410 storeBatch.Set(Key(assetID), rawAsset)
411 storeBatch.Set(aliasKey(newAlias), []byte(assetID.String()))
412 storeBatch.Delete(aliasKey(oldAlias))
416 reg.aliasCache.Add(newAlias, assetID)
417 reg.aliasCache.Remove(oldAlias)