7 log "github.com/sirupsen/logrus"
8 "github.com/tendermint/tmlibs/db"
10 "github.com/bytom/account"
11 "github.com/bytom/asset"
12 "github.com/bytom/blockchain/query"
13 "github.com/bytom/blockchain/signers"
14 "github.com/bytom/common"
15 "github.com/bytom/consensus"
16 "github.com/bytom/crypto/sha3pool"
17 chainjson "github.com/bytom/encoding/json"
18 "github.com/bytom/protocol/bc"
19 "github.com/bytom/protocol/bc/types"
20 "github.com/bytom/protocol/vm/vmutil"
23 // annotateTxs adds asset data to transactions
24 func annotateTxsAsset(w *Wallet, txs []*query.AnnotatedTx) {
25 for i, tx := range txs {
26 for j, input := range tx.Inputs {
27 txs[i].Inputs[j].AssetAlias, txs[i].Inputs[j].AssetDefinition =
28 w.getAliasDefinition(input.AssetID)
30 for k, output := range tx.Outputs {
31 txs[i].Outputs[k].AssetAlias, txs[i].Outputs[k].AssetDefinition =
32 w.getAliasDefinition(output.AssetID)
37 func (w *Wallet) getExternalDefinition(assetID *bc.AssetID) *chainjson.HexBytes {
39 definitionByte := w.DB.Get(asset.CalcExtAssetKey(assetID))
40 if definitionByte == nil {
44 definitionMap := make(map[string]interface{})
45 if err := json.Unmarshal(definitionByte, &definitionMap); err != nil {
49 saveAlias := assetID.String()
50 storeBatch := w.DB.NewBatch()
52 externalAsset := &asset.Asset{AssetID: *assetID, Alias: &saveAlias, DefinitionMap: definitionMap, Signer: &signers.Signer{Type: "external"}}
53 if rawAsset, err := json.Marshal(externalAsset); err == nil {
54 log.WithFields(log.Fields{"assetID": assetID.String(), "alias": saveAlias}).Info("index external asset")
55 storeBatch.Set(asset.Key(assetID), rawAsset)
57 storeBatch.Set(asset.AliasKey(saveAlias), []byte(assetID.String()))
60 d := chainjson.HexBytes(definitionByte)
65 func (w *Wallet) getAliasDefinition(assetID bc.AssetID) (string, *chainjson.HexBytes) {
67 if assetID.String() == consensus.BTMAssetID.String() {
68 alias := consensus.BTMAlias
69 definition := &asset.DefaultNativeAsset.RawDefinitionByte
71 return alias, definition
74 //local asset and saved external asset
75 if localAsset, err := w.AssetReg.FindByID(nil, &assetID); err == nil {
76 alias := *localAsset.Alias
77 definition := &localAsset.RawDefinitionByte
78 return alias, definition
82 if definition := w.getExternalDefinition(&assetID); definition != nil {
83 return assetID.String(), definition
89 // annotateTxs adds account data to transactions
90 func annotateTxsAccount(txs []*query.AnnotatedTx, walletDB db.DB) {
91 for i, tx := range txs {
92 for j, input := range tx.Inputs {
93 //issue asset tx input SpentOutputID is nil
94 if input.SpentOutputID == nil {
97 localAccount, err := getAccountFromUTXO(*input.SpentOutputID, walletDB)
98 if localAccount == nil || err != nil {
101 txs[i].Inputs[j].AccountAlias = localAccount.Alias
102 txs[i].Inputs[j].AccountID = localAccount.ID
104 for j, output := range tx.Outputs {
105 localAccount, err := getAccountFromACP(output.ControlProgram, walletDB)
106 if localAccount == nil || err != nil {
109 txs[i].Outputs[j].AccountAlias = localAccount.Alias
110 txs[i].Outputs[j].AccountID = localAccount.ID
115 func getAccountFromUTXO(outputID bc.Hash, walletDB db.DB) (*account.Account, error) {
116 accountUTXO := account.UTXO{}
117 localAccount := account.Account{}
119 accountUTXOValue := walletDB.Get(account.StandardUTXOKey(outputID))
120 if accountUTXOValue == nil {
121 return nil, fmt.Errorf("failed get account utxo:%x ", outputID)
124 if err := json.Unmarshal(accountUTXOValue, &accountUTXO); err != nil {
128 accountValue := walletDB.Get(account.Key(accountUTXO.AccountID))
129 if accountValue == nil {
130 return nil, fmt.Errorf("failed get account:%s ", accountUTXO.AccountID)
132 if err := json.Unmarshal(accountValue, &localAccount); err != nil {
136 return &localAccount, nil
139 func getAccountFromACP(program []byte, walletDB db.DB) (*account.Account, error) {
141 accountCP := account.CtrlProgram{}
142 localAccount := account.Account{}
144 sha3pool.Sum256(hash[:], program)
146 rawProgram := walletDB.Get(account.CPKey(hash))
147 if rawProgram == nil {
148 return nil, fmt.Errorf("failed get account control program:%x ", hash)
151 if err := json.Unmarshal(rawProgram, &accountCP); err != nil {
155 accountValue := walletDB.Get(account.Key(accountCP.AccountID))
156 if accountValue == nil {
157 return nil, fmt.Errorf("failed get account:%s ", accountCP.AccountID)
160 if err := json.Unmarshal(accountValue, &localAccount); err != nil {
164 return &localAccount, nil
167 var emptyJSONObject = chainjson.HexBytes(`{}`)
169 func isValidJSON(b []byte) bool {
171 err := json.Unmarshal(b, &v)
175 func buildAnnotatedTransaction(orig *types.Tx, b *types.Block, statusFail bool, indexInBlock int) *query.AnnotatedTx {
176 tx := &query.AnnotatedTx{
178 Timestamp: b.Timestamp,
180 BlockHeight: b.Height,
181 Position: uint32(indexInBlock),
182 BlockTransactionsCount: uint32(len(b.Transactions)),
183 Inputs: make([]*query.AnnotatedInput, 0, len(orig.Inputs)),
184 Outputs: make([]*query.AnnotatedOutput, 0, len(orig.Outputs)),
185 StatusFail: statusFail,
187 for i := range orig.Inputs {
188 tx.Inputs = append(tx.Inputs, BuildAnnotatedInput(orig, uint32(i)))
190 for i := range orig.Outputs {
191 tx.Outputs = append(tx.Outputs, BuildAnnotatedOutput(orig, i))
196 // BuildAnnotatedInput build the annotated input.
197 func BuildAnnotatedInput(tx *types.Tx, i uint32) *query.AnnotatedInput {
199 in := &query.AnnotatedInput{
200 AssetDefinition: &emptyJSONObject,
202 if !orig.IsCoinbase() {
203 in.AssetID = orig.AssetID()
204 in.Amount = orig.Amount()
207 id := tx.Tx.InputIDs[i]
209 switch e := e.(type) {
212 in.ControlProgram = orig.ControlProgram()
213 in.SpentOutputID = e.SpentOutputId
216 in.IssuanceProgram = orig.IssuanceProgram()
219 in.Arbitrary = e.Arbitrary
224 // BuildAnnotatedOutput build the annotated output.
225 func BuildAnnotatedOutput(tx *types.Tx, idx int) *query.AnnotatedOutput {
226 orig := tx.Outputs[idx]
227 outid := tx.OutputID(idx)
228 out := &query.AnnotatedOutput{
231 AssetID: *orig.AssetId,
232 AssetDefinition: &emptyJSONObject,
234 ControlProgram: orig.ControlProgram,
236 if vmutil.IsUnspendable(out.ControlProgram) {