}
storeBatch := m.db.NewBatch()
+
+ m.cacheMu.Lock()
m.aliasCache.Remove(account.Alias)
+ m.cacheMu.Unlock()
+
storeBatch.Delete(aliasKey(account.Alias))
storeBatch.Delete(Key(account.ID))
storeBatch.Write()
+
return nil
}
jsonTags := json.RawMessage(`{}`)
jsonDefinition := json.RawMessage(`{}`)
- // a.RawDefinition is the asset definition as it appears on the
+ // a.RawDefinitionByte is the asset definition as it appears on the
// blockchain, so it's untrusted and may not be valid json.
- if isValidJSON(a.RawDefinition()) {
- jsonDefinition = json.RawMessage(a.RawDefinition())
+ if isValidJSON(a.RawDefinitionByte) {
+ jsonDefinition = json.RawMessage(a.RawDefinitionByte)
}
if a.Tags != nil {
"golang.org/x/crypto/sha3"
"github.com/bytom/blockchain/signers"
+ cfg "github.com/bytom/config"
"github.com/bytom/consensus"
"github.com/bytom/crypto/ed25519"
"github.com/bytom/crypto/ed25519/chainkd"
"github.com/bytom/protocol/vm/vmutil"
)
+func init() {
+ DefaultNativeAsset = generateNativeAsset()
+}
+
+var DefaultNativeAsset *Asset
+
const (
maxAssetCache = 1000
assetPrefix = "ASS:"
- aliasPrefix = "ALS:"
+ //AliasPrefix is asset alias prefix
+ AliasPrefix = "ALS:"
+ //ExternalAssetPrefix is external definition assets prefix
+ ExternalAssetPrefix = "EXA"
indexPrefix = "ASSIDX:"
)
-func aliasKey(name string) []byte {
- return []byte(aliasPrefix + name)
+func generateNativeAsset() *Asset {
+ genesisBlock := cfg.GenerateGenesisBlock()
+ signer := &signers.Signer{Type: "internal"}
+ alias := consensus.BTMAlias
+
+ definitionBytes, _ := serializeAssetDef(consensus.BTMDefinitionMap)
+
+ return &Asset{
+ Signer: signer,
+ AssetID: *consensus.BTMAssetID,
+ Alias: &alias,
+ VMVersion: 1,
+ DefinitionMap: consensus.BTMDefinitionMap,
+ RawDefinitionByte: definitionBytes,
+ InitialBlockHash: genesisBlock.Hash()}
+}
+
+func AliasKey(name string) []byte {
+ return []byte(AliasPrefix + name)
}
//Key asset store prefix
return []byte(indexPrefix + xpub.String())
}
+//CalcExtAssetKey return store external assets key
+func CalcExtAssetKey(id *bc.AssetID) []byte {
+ name := id.String()
+ return []byte(ExternalAssetPrefix + name)
+}
+
// pre-define errors for supporting bytom errorFormatter
var (
ErrDuplicateAlias = errors.New("duplicate asset alias")
ErrMarshalAsset = errors.New("failed marshal asset")
ErrFindAsset = errors.New("fail to find asset")
ErrInternalAsset = errors.New("btm has been defined as the internal asset")
+ ErrNullAlias = errors.New("null asset alias")
)
//NewRegistry create new registry
IssuanceProgram chainjson.HexBytes `json:"issue_program"`
InitialBlockHash bc.Hash `json:"init_blockhash"`
Tags map[string]interface{} `json:"tags"`
- RawDefinitionByte []byte `json:"raw_definition_byte"`
+ RawDefinitionByte chainjson.HexBytes `json:"raw_definition_byte"`
DefinitionMap map[string]interface{} `json:"definition"`
}
-//RawDefinition return asset in the raw format
-func (asset *Asset) RawDefinition() []byte {
- return asset.RawDefinitionByte
-}
-
func (reg *Registry) getNextAssetIndex(xpubs []chainkd.XPub) (*uint64, error) {
reg.assetIndexMu.Lock()
defer reg.assetIndexMu.Unlock()
// Define defines a new Asset.
func (reg *Registry) Define(xpubs []chainkd.XPub, quorum int, definition map[string]interface{}, alias string, tags map[string]interface{}) (*Asset, error) {
- if alias == "btm" {
+ if alias == consensus.BTMAlias {
return nil, ErrInternalAsset
}
- if existed := reg.db.Get(aliasKey(alias)); existed != nil {
+ if existed := reg.db.Get(AliasKey(alias)); existed != nil {
return nil, ErrDuplicateAlias
}
}
storeBatch := reg.db.NewBatch()
- storeBatch.Set(aliasKey(alias), []byte(asset.AssetID.String()))
+ storeBatch.Set(AliasKey(alias), []byte(asset.AssetID.String()))
storeBatch.Set(Key(&asset.AssetID), ass)
storeBatch.Write()
if err := assetID.UnmarshalText([]byte(assetInfo)); err != nil {
return err
}
- if asset, err = reg.findByID(ctx, assetID); err != nil {
+ if asset, err = reg.FindByID(ctx, assetID); err != nil {
return err
}
}
}
// findByID retrieves an Asset record along with its signer, given an assetID.
-func (reg *Registry) findByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
+func (reg *Registry) FindByID(ctx context.Context, id *bc.AssetID) (*Asset, error) {
reg.cacheMu.Lock()
cached, ok := reg.cache.Get(id.String())
reg.cacheMu.Unlock()
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) {
cachedID, ok := reg.aliasCache.Get(alias)
reg.cacheMu.Unlock()
if ok {
- return reg.findByID(ctx, cachedID.(*bc.AssetID))
+ return reg.FindByID(ctx, 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(ctx, assetID)
}
+//GetAliasByID return asset alias string by AssetID string
func (reg *Registry) GetAliasByID(id string) string {
+ //btm
if id == consensus.BTMAssetID.String() {
- return "btm"
+ return consensus.BTMAlias
}
+
assetID := &bc.AssetID{}
if err := assetID.UnmarshalText([]byte(id)); err != nil {
return ""
}
- asset, err := reg.findByID(nil, assetID)
+
+ asset, err := reg.FindByID(nil, assetID)
if err != nil {
return ""
}
+
return *asset.Alias
}
// ListAssets returns the accounts in the db
func (reg *Registry) ListAssets(id string) ([]*Asset, error) {
- assets := []*Asset{}
+ assets := []*Asset{DefaultNativeAsset}
assetIter := reg.db.IteratorPrefix([]byte(assetPrefix + id))
defer assetIter.Release()
prog, err := builder.Build()
return prog, 1, err
}
+
+//UpdateAssetAlias updates oldAlias to newAlias
+func (reg *Registry) UpdateAssetAlias(oldAlias, newAlias string) error {
+ if oldAlias == consensus.BTMAlias || newAlias == consensus.BTMAlias {
+ return ErrInternalAsset
+ }
+
+ if oldAlias == "" || newAlias == "" {
+ return ErrNullAlias
+ }
+
+ if _, err := reg.GetIDByAlias(newAlias); err == nil {
+ return ErrDuplicateAlias
+ }
+
+ findAsset, err := reg.FindByAlias(nil, oldAlias)
+ if err != nil {
+ return err
+ }
+
+ storeBatch := reg.db.NewBatch()
+ findAsset.Alias = &newAlias
+ assetID := &findAsset.AssetID
+ rawAsset, err := json.Marshal(findAsset)
+ if err != nil {
+ return err
+ }
+
+ storeBatch.Set(Key(assetID), rawAsset)
+ storeBatch.Set(AliasKey(newAlias), []byte(assetID.String()))
+ storeBatch.Delete(AliasKey(oldAlias))
+ storeBatch.Write()
+
+ reg.cacheMu.Lock()
+ reg.aliasCache.Add(newAlias, assetID)
+ reg.aliasCache.Remove(oldAlias)
+ reg.cacheMu.Unlock()
+
+ return nil
+}
testutil.FatalErr(t, err)
}
- found, err := reg.findByID(ctx, &asset.AssetID)
+ found, err := reg.FindByID(ctx, &asset.AssetID)
if err != nil {
t.Errorf("unexpected error %v", err)
}
testutil.FatalErr(t, err)
}
- found, err := reg.findByID(ctx, &asset.AssetID)
+ found, err := reg.FindByID(ctx, &asset.AssetID)
if err != nil {
testutil.FatalErr(t, err)
}
return txbuilder.MissingFieldsError("asset_id")
}
- asset, err := a.assets.findByID(ctx, a.AssetId)
+ asset, err := a.assets.FindByID(ctx, a.AssetId)
if err != nil {
return err
}
return err
}
- assetDef := asset.RawDefinition()
+ assetDef := asset.RawDefinitionByte
txin := legacy.NewIssuanceInput(nonce[:], a.Amount, a.ReferenceData, asset.InitialBlockHash, asset.IssuanceProgram, nil, assetDef)
return resWrapper(nil)
}
+
+// POST /update-asset-alias
+func (bcr *BlockchainReactor) updateAssetAlias(updateAlias struct {
+ OldAlias string `json:"old_alias"`
+ NewAlias string `json:"new_alias"`
+}) Response {
+ if err := bcr.assets.UpdateAssetAlias(updateAlias.OldAlias, updateAlias.NewAlias); err != nil {
+ return resWrapper(nil, err)
+ }
+
+ return resWrapper(nil)
+}
AssetID bc.AssetID `json:"asset_id"`
AssetAlias string `json:"asset_alias,omitempty"`
AssetDefinition *json.RawMessage `json:"asset_definition"`
- AssetTags *json.RawMessage `json:"asset_tags,omitempty"`
Amount uint64 `json:"amount"`
IssuanceProgram chainjson.HexBytes `json:"issuance_program,omitempty"`
ControlProgram chainjson.HexBytes `json:"-"`
SpentOutputID *bc.Hash `json:"spent_output_id,omitempty"`
AccountID string `json:"account_id,omitempty"`
AccountAlias string `json:"account_alias,omitempty"`
- AccountTags *json.RawMessage `json:"account_tags,omitempty"`
ReferenceData *json.RawMessage `json:"reference_data"`
Arbitrary chainjson.HexBytes `json:"arbitrary,omitempty"`
}
AssetID bc.AssetID `json:"asset_id"`
AssetAlias string `json:"asset_alias,omitempty"`
AssetDefinition *json.RawMessage `json:"asset_definition"`
- AssetTags *json.RawMessage `json:"asset_tags"`
Amount uint64 `json:"amount"`
AccountID string `json:"account_id,omitempty"`
AccountAlias string `json:"account_alias,omitempty"`
- AccountTags *json.RawMessage `json:"account_tags,omitempty"`
ControlProgram chainjson.HexBytes `json:"control_program"`
ReferenceData *json.RawMessage `json:"reference_data"`
}
var (
errBadActionType = errors.New("bad action type")
- errBadAlias = errors.New("bad alias")
errBadAction = errors.New("bad action object")
)
alias, _ := m["asset_alias"].(string)
if id == "" && alias != "" {
switch alias {
- case "btm":
+ case consensus.BTMAlias:
m["asset_id"] = consensus.BTMAssetID.String()
default:
- asset, err := bcr.assets.FindByAlias(ctx, alias)
+ id, err := bcr.assets.GetIDByAlias(alias)
if err != nil {
return errors.WithDetailf(err, "invalid asset alias %s on action %d", alias, i)
}
- m["asset_id"] = asset.AssetID.String()
+ m["asset_id"] = id
}
}
m.Handle("/delete-account", jsonHandler(bcr.deleteAccount))
m.Handle("/create-asset", jsonHandler(bcr.createAsset))
+ m.Handle("/update-asset-alias", jsonHandler(bcr.updateAssetAlias))
m.Handle("/update-asset-tags", jsonHandler(bcr.updateAssetTags))
m.Handle("/list-assets", jsonHandler(bcr.listAssets))
AssetID: orig.AssetID(),
Amount: orig.Amount(),
AssetDefinition: &emptyJSONObject,
- AssetTags: &emptyJSONObject,
ReferenceData: &emptyJSONObject,
}
Position: idx,
AssetID: *orig.AssetId,
AssetDefinition: &emptyJSONObject,
- AssetTags: &emptyJSONObject,
Amount: orig.Amount,
ControlProgram: orig.ControlProgram,
ReferenceData: &emptyJSONObject,
import (
"context"
+ "bytes"
"github.com/bytom/crypto/ed25519/chainkd"
- "github.com/tendermint/go-wire/data/base58"
- "chain/errors"
"github.com/bytom/crypto/sha3pool"
- "bytes"
+ "github.com/bytom/errors"
+ "github.com/tendermint/go-wire/data/base58"
)
type KeyImportParams struct {
- KeyAlias string `json:"alias"`
- Password string `json:"password"`
- XPrv string `json:"xprv"`
- Index uint64 `json:"index"`
- AccountAlias string `json:"account_alias"`
+ KeyAlias string `json:"alias"`
+ Password string `json:"password"`
+ XPrv string `json:"xprv"`
+ Index uint64 `json:"index"`
+ AccountAlias string `json:"account_alias"`
}
func (bcr *BlockchainReactor) walletExportKey(ctx context.Context, in struct {
"github.com/bytom/blockchain/account"
"github.com/bytom/blockchain/asset"
"github.com/bytom/blockchain/query"
+ "github.com/bytom/blockchain/signers"
"github.com/bytom/common"
+ "github.com/bytom/consensus"
"github.com/bytom/crypto/sha3pool"
"github.com/bytom/errors"
"github.com/bytom/protocol/bc"
)
// annotateTxs adds asset data to transactions
-func annotateTxsAsset(txs []*query.AnnotatedTx, walletDB db.DB) {
+func annotateTxsAsset(w *Wallet, txs []*query.AnnotatedTx) {
for i, tx := range txs {
for j, input := range tx.Inputs {
- localAsset, err := getAliasFromAssetID(input.AssetID, walletDB)
- if localAsset == nil || err != nil {
+ alias, definition, err := w.getAliasDefinition(input.AssetID)
+ if err != nil {
continue
}
- txs[i].Inputs[j].AssetAlias = *localAsset.Alias
+ txs[i].Inputs[j].AssetAlias = alias
+ txs[i].Inputs[j].AssetDefinition = &definition
}
for j, output := range tx.Outputs {
- localAsset, err := getAliasFromAssetID(output.AssetID, walletDB)
- if localAsset == nil || err != nil {
+ alias, definition, err := w.getAliasDefinition(output.AssetID)
+ if err != nil {
continue
}
- txs[i].Outputs[j].AssetAlias = *localAsset.Alias
+ txs[i].Outputs[j].AssetAlias = alias
+ txs[i].Outputs[j].AssetDefinition = &definition
}
}
}
-func getAliasFromAssetID(assetID bc.AssetID, walletDB db.DB) (*asset.Asset, error) {
- var localAsset asset.Asset
- rawAsset := walletDB.Get(asset.Key(&assetID))
- if rawAsset == nil {
+func (w *Wallet) getExternalDefinition(assetID *bc.AssetID) (json.RawMessage, error) {
+
+ definitionByte := w.DB.Get(asset.CalcExtAssetKey(assetID))
+ if definitionByte == nil {
return nil, nil
}
- if err := json.Unmarshal(rawAsset, &localAsset); err != nil {
- log.WithFields(log.Fields{"warn": err, "asset id": assetID.String()}).Warn("look up asset")
+ definitionMap := make(map[string]interface{})
+ if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
return nil, err
}
- return &localAsset, nil
+ saveAlias := assetID.String()
+ storeBatch := w.DB.NewBatch()
+
+ externalAsset := &asset.Asset{AssetID: *assetID, Alias: &saveAlias, DefinitionMap: definitionMap, Signer: &signers.Signer{Type: "external"}}
+ if rawAsset, err := json.Marshal(externalAsset); err == nil {
+ log.WithFields(log.Fields{"assetID": assetID.String(), "alias": saveAlias}).Info("index external asset")
+ storeBatch.Set(asset.Key(assetID), rawAsset)
+ }
+ storeBatch.Set(asset.AliasKey(saveAlias), []byte(assetID.String()))
+ storeBatch.Write()
+
+ return definitionByte, nil
+
+}
+
+func (w *Wallet) getAliasDefinition(assetID bc.AssetID) (string, json.RawMessage, error) {
+ //btm
+ if assetID.String() == consensus.BTMAssetID.String() {
+ alias := consensus.BTMAlias
+ definition := []byte(asset.DefaultNativeAsset.RawDefinitionByte)
+
+ return alias, definition, nil
+ }
+
+ //local asset and saved external asset
+ localAsset, err := w.AssetReg.FindByID(nil, &assetID)
+ if err != nil {
+ return "", nil, err
+ }
+ alias := *localAsset.Alias
+ definition := []byte(localAsset.RawDefinitionByte)
+ return alias, definition, nil
+
+ //external asset
+ if definition, err := w.getExternalDefinition(&assetID); definition != nil {
+ return assetID.String(), definition, err
+ }
+
+ return "", nil, fmt.Errorf("look up asset %s :not found ", assetID.String())
}
// annotateTxs adds account data to transactions
if input.SpentOutputID == nil {
continue
}
- account, err := getAccountFromUTXO(*input.SpentOutputID, walletDB)
- if account == nil || err != nil {
+ localAccount, err := getAccountFromUTXO(*input.SpentOutputID, walletDB)
+ if localAccount == nil || err != nil {
continue
}
- txs[i].Inputs[j].AccountAlias = account.Alias
- txs[i].Inputs[j].AccountID = account.ID
+ txs[i].Inputs[j].AccountAlias = localAccount.Alias
+ txs[i].Inputs[j].AccountID = localAccount.ID
}
for j, output := range tx.Outputs {
- account, err := getAccountFromACP(output.ControlProgram, walletDB)
- if account == nil || err != nil {
+ localAccount, err := getAccountFromACP(output.ControlProgram, walletDB)
+ if localAccount == nil || err != nil {
continue
}
- txs[i].Outputs[j].AccountAlias = account.Alias
- txs[i].Outputs[j].AccountID = account.ID
+ txs[i].Outputs[j].AccountAlias = localAccount.Alias
+ txs[i].Outputs[j].AccountID = localAccount.ID
}
}
}
orig := tx.Inputs[i]
in := &query.AnnotatedInput{
AssetDefinition: &emptyJSONObject,
- AssetTags: &emptyJSONObject,
ReferenceData: &emptyJSONObject,
}
if !orig.IsCoinbase() {
Position: idx,
AssetID: *orig.AssetId,
AssetDefinition: &emptyJSONObject,
- AssetTags: &emptyJSONObject,
Amount: orig.Amount,
ControlProgram: orig.ControlProgram,
ReferenceData: &emptyJSONObject,
"github.com/tendermint/tmlibs/db"
"github.com/bytom/blockchain/account"
+ "github.com/bytom/blockchain/asset"
"github.com/bytom/blockchain/query"
"github.com/bytom/crypto/sha3pool"
"github.com/bytom/errors"
const (
//TxPrefix is wallet database transactions prefix
TxPrefix = "TXS:"
- //TxIndex is wallet database tx index prefix
- TxIndex = "TID:"
+ //TxIndexPrefix is wallet database tx index prefix
+ TxIndexPrefix = "TID:"
)
func formatKey(blockHeight uint64, position uint32) string {
}
func calcTxIndexKey(txID string) []byte {
- return []byte(TxIndex + txID)
+ return []byte(TxIndexPrefix + txID)
}
//deleteTransaction delete transactions when orphan block rollback
}
}
+//save external assets definition
+func saveExternalAssetDefinition(b *legacy.Block, walletDB db.DB) {
+ storeBatch := walletDB.NewBatch()
+ defer storeBatch.Write()
+
+ for _, tx := range b.Transactions {
+ for _, orig := range tx.Inputs {
+ if ii, ok := orig.TypedInput.(*legacy.IssuanceInput); ok {
+ if isValidJSON(ii.AssetDefinition) {
+ assetID := ii.AssetID()
+ if assetExist := walletDB.Get(asset.CalcExtAssetKey(&assetID)); assetExist != nil {
+ continue
+ }
+ storeBatch.Set(asset.CalcExtAssetKey(&assetID), ii.AssetDefinition)
+ }
+ }
+ }
+ }
+}
+
//indexTransactions saves all annotated transactions to the database.
func (w *Wallet) indexTransactions(batch db.Batch, b *legacy.Block) error {
annotatedTxs := filterAccountTxs(b, w)
- annotateTxsAsset(annotatedTxs, w.DB)
+ saveExternalAssetDefinition(b, w.DB)
+ annotateTxsAsset(w, annotatedTxs)
annotateTxsAccount(annotatedTxs, w.DB)
for _, tx := range annotatedTxs {
jww.FEEDBACK.Println("Successfully update asset tags")
},
}
+
+var updateAssetAliasCmd = &cobra.Command{
+ Use: "update-asset-alias <oldAlias> <newAlias>",
+ Short: "Update the asset alias",
+ Args: cobra.ExactArgs(2),
+ Run: func(cmd *cobra.Command, args []string) {
+ var updateAlias = struct {
+ OldAlias string `json:"old_alias"`
+ NewAlias string `json:"new_alias"`
+ }{OldAlias: args[0], NewAlias: args[1]}
+
+ if _, exitCode := util.ClientCall("/update-asset-alias", &updateAlias); exitCode != util.Success {
+ os.Exit(exitCode)
+ }
+
+ jww.FEEDBACK.Println("Successfully update asset alias")
+ },
+}
BytomcliCmd.AddCommand(createAssetCmd)
BytomcliCmd.AddCommand(listAssetsCmd)
BytomcliCmd.AddCommand(updateAssetTagsCmd)
+ BytomcliCmd.AddCommand(updateAssetAliasCmd)
BytomcliCmd.AddCommand(listTransactionsCmd)
BytomcliCmd.AddCommand(listUnspentOutputsCmd)
if cmn.FileExists(genDocFile) {
jsonBlob, err := ioutil.ReadFile(genDocFile)
if err != nil {
- return fmt.Errorf("Couldn't read GenesisDoc file: %v", err)
+ return fmt.Errorf("Couldn't read GenesisDoc file: %v ", err)
}
genDoc, err := types.GenesisDocFromJSON(jsonBlob)
if err != nil {
- return fmt.Errorf("Error reading GenesisDoc: %v", err)
+ return fmt.Errorf("Error reading GenesisDoc: %v ", err)
}
if genDoc.ChainID == "" {
- return fmt.Errorf("Genesis doc %v must include non-empty chain_id", genDocFile)
+ return fmt.Errorf("Genesis doc %v must include non-empty chain_id ", genDocFile)
}
+
config.ChainID = genDoc.ChainID
config.PrivateKey = genDoc.PrivateKey
config.Time = genDoc.GenesisTime
V3: uint64(18446744073709551615),
}
+//BTMAlias is default btm
+var BTMAlias = "btm"
+
+//BTMSymbol
+var BTMSymbol = "btm"
+
+//BTMDecimals
+var BTMDecimals = 8
+
+//BTMDescription
+var BTMDescription = `Bytom Official Issue`
+
+//BTMDefinitionMap
+var BTMDefinitionMap = map[string]interface{}{
+ "name": BTMAlias,
+ "symbol": BTMSymbol,
+ "decimals": BTMDecimals,
+ "description": BTMDescription,
+}
+
// BlockSubsidy calculate the coinbase rewards on given block height
func BlockSubsidy(height uint64) uint64 {
if height == 0 {