OSDN Git Service

2fb1adce3a09e4eb8c77d28dbaba08792c0f29ba
[bytom/vapor.git] / wallet / annotated.go
1 package wallet
2
3 import (
4         "encoding/json"
5
6         btmCommon "github.com/bytom/common"
7         btmConsensus "github.com/bytom/consensus"
8         log "github.com/sirupsen/logrus"
9
10         "github.com/vapor/account"
11         "github.com/vapor/asset"
12         "github.com/vapor/blockchain/query"
13         "github.com/vapor/common"
14         "github.com/vapor/consensus"
15         "github.com/vapor/consensus/segwit"
16         "github.com/vapor/crypto/sha3pool"
17         "github.com/vapor/protocol/bc"
18         "github.com/vapor/protocol/bc/types"
19 )
20
21 // annotateTxs adds asset data to transactions
22 func annotateTxsAsset(w *Wallet, txs []*query.AnnotatedTx) {
23         for i, tx := range txs {
24                 for j, input := range tx.Inputs {
25                         alias, definition := w.getAliasDefinition(input.AssetID)
26                         txs[i].Inputs[j].AssetAlias, txs[i].Inputs[j].AssetDefinition = alias, &definition
27                 }
28                 for k, output := range tx.Outputs {
29                         alias, definition := w.getAliasDefinition(output.AssetID)
30                         txs[i].Outputs[k].AssetAlias, txs[i].Outputs[k].AssetDefinition = alias, &definition
31                 }
32         }
33 }
34
35 func (w *Wallet) getExternalDefinition(assetID *bc.AssetID) json.RawMessage {
36         externalAsset, err := w.Store.GetAsset(assetID)
37         if err != nil && err != ErrGetAsset {
38                 log.WithFields(log.Fields{"module": logModule, "err": err, "assetID": assetID.String()}).Info("fail on get asset definition.")
39         }
40         if externalAsset == nil {
41                 return nil
42         }
43
44         if err := w.AssetReg.SaveAsset(externalAsset, *externalAsset.Alias); err != nil {
45                 log.WithFields(log.Fields{"module": logModule, "err": err, "assetAlias": *externalAsset.Alias}).Info("fail on save external asset to internal asset DB")
46         }
47         return json.RawMessage(externalAsset.RawDefinitionByte)
48 }
49
50 func (w *Wallet) getAliasDefinition(assetID bc.AssetID) (string, json.RawMessage) {
51         //btm
52         if assetID.String() == consensus.BTMAssetID.String() {
53                 alias := consensus.BTMAlias
54                 definition := []byte(asset.DefaultNativeAsset.RawDefinitionByte)
55
56                 return alias, definition
57         }
58
59         //local asset and saved external asset
60         if localAsset, err := w.AssetReg.FindByID(nil, &assetID); err == nil {
61                 alias := *localAsset.Alias
62                 definition := []byte(localAsset.RawDefinitionByte)
63                 return alias, definition
64         }
65
66         //external asset
67         if definition := w.getExternalDefinition(&assetID); definition != nil {
68                 return assetID.String(), definition
69         }
70
71         return "", nil
72 }
73
74 // annotateTxs adds account data to transactions
75 func (w *Wallet) annotateTxsAccount(txs []*query.AnnotatedTx) {
76         for i, tx := range txs {
77                 for j, input := range tx.Inputs {
78                         //issue asset tx input SpentOutputID is nil
79                         if input.SpentOutputID == nil {
80                                 continue
81                         }
82                         localAccount, err := w.getAccountFromACP(input.ControlProgram)
83                         if localAccount == nil || err != nil {
84                                 continue
85                         }
86                         txs[i].Inputs[j].AccountAlias = localAccount.Alias
87                         txs[i].Inputs[j].AccountID = localAccount.ID
88                 }
89                 for j, output := range tx.Outputs {
90                         localAccount, err := w.getAccountFromACP(output.ControlProgram)
91                         if localAccount == nil || err != nil {
92                                 continue
93                         }
94                         txs[i].Outputs[j].AccountAlias = localAccount.Alias
95                         txs[i].Outputs[j].AccountID = localAccount.ID
96                 }
97         }
98 }
99
100 func (w *Wallet) getAccountFromACP(program []byte) (*account.Account, error) {
101         var hash [32]byte
102         sha3pool.Sum256(hash[:], program)
103         accountCP, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash))
104         if err != nil {
105                 return nil, err
106         }
107
108         account, err := w.AccountMgr.FindByID(accountCP.AccountID)
109         if err != nil {
110                 return nil, err
111         }
112
113         return account, nil
114 }
115
116 var emptyJSONObject = json.RawMessage(`{}`)
117
118 func (w *Wallet) buildAnnotatedTransaction(orig *types.Tx, b *types.Block, statusFail bool, indexInBlock int) *query.AnnotatedTx {
119         tx := &query.AnnotatedTx{
120                 ID:                     orig.ID,
121                 Timestamp:              b.Timestamp,
122                 BlockID:                b.Hash(),
123                 BlockHeight:            b.Height,
124                 Position:               uint32(indexInBlock),
125                 BlockTransactionsCount: uint32(len(b.Transactions)),
126                 Inputs:                 make([]*query.AnnotatedInput, 0, len(orig.Inputs)),
127                 Outputs:                make([]*query.AnnotatedOutput, 0, len(orig.Outputs)),
128                 StatusFail:             statusFail,
129                 Size:                   orig.SerializedSize,
130         }
131         for i := range orig.Inputs {
132                 tx.Inputs = append(tx.Inputs, w.BuildAnnotatedInput(orig, uint32(i)))
133         }
134         for i := range orig.Outputs {
135                 tx.Outputs = append(tx.Outputs, w.BuildAnnotatedOutput(orig, i))
136         }
137         return tx
138 }
139
140 // BuildAnnotatedInput build the annotated input.
141 func (w *Wallet) BuildAnnotatedInput(tx *types.Tx, i uint32) *query.AnnotatedInput {
142         orig := tx.Inputs[i]
143         in := &query.AnnotatedInput{
144                 AssetDefinition: &emptyJSONObject,
145         }
146         if orig.InputType() != types.CoinbaseInputType {
147                 in.AssetID = orig.AssetID()
148                 in.Amount = orig.Amount()
149         } else {
150                 in.AssetID = *consensus.BTMAssetID
151         }
152
153         id := tx.Tx.InputIDs[i]
154         in.InputID = id
155         e := tx.Entries[id]
156         switch e := e.(type) {
157         case *bc.VetoInput:
158                 in.Type = "veto"
159                 in.ControlProgram = orig.ControlProgram()
160                 in.Address = w.getAddressFromControlProgram(in.ControlProgram, false)
161                 in.SpentOutputID = e.SpentOutputId
162                 arguments := orig.Arguments()
163                 for _, arg := range arguments {
164                         in.WitnessArguments = append(in.WitnessArguments, arg)
165                 }
166
167         case *bc.CrossChainInput:
168                 in.Type = "cross_chain_in"
169                 in.ControlProgram = orig.ControlProgram()
170                 in.Address = w.getAddressFromControlProgram(in.ControlProgram, true)
171                 in.SpentOutputID = e.MainchainOutputId
172                 arguments := orig.Arguments()
173                 for _, arg := range arguments {
174                         in.WitnessArguments = append(in.WitnessArguments, arg)
175                 }
176
177         case *bc.Spend:
178                 in.Type = "spend"
179                 in.ControlProgram = orig.ControlProgram()
180                 in.Address = w.getAddressFromControlProgram(in.ControlProgram, false)
181                 in.SpentOutputID = e.SpentOutputId
182                 arguments := orig.Arguments()
183                 for _, arg := range arguments {
184                         in.WitnessArguments = append(in.WitnessArguments, arg)
185                 }
186
187         case *bc.Coinbase:
188                 in.Type = "coinbase"
189                 in.Arbitrary = e.Arbitrary
190         }
191         return in
192 }
193
194 func (w *Wallet) getAddressFromControlProgram(prog []byte, isMainchain bool) string {
195         var netParams interface{}
196         netParams = &consensus.ActiveNetParams
197         if isMainchain {
198                 netParams = &btmConsensus.MainNetParams
199         }
200
201         if segwit.IsP2WPKHScript(prog) {
202                 if pubHash, err := segwit.GetHashFromStandardProg(prog); err == nil {
203                         return BuildP2PKHAddress(pubHash, netParams)
204                 }
205         } else if segwit.IsP2WSHScript(prog) {
206                 if scriptHash, err := segwit.GetHashFromStandardProg(prog); err == nil {
207                         return BuildP2SHAddress(scriptHash, netParams)
208                 }
209         }
210
211         return ""
212 }
213
214 // BuildP2PKHAddress create P2PKH address
215 func BuildP2PKHAddress(pubHash []byte, netParams interface{}) string {
216         switch netParams.(type) {
217         case *consensus.Params:
218                 params := netParams.(*consensus.Params)
219                 address, err := common.NewAddressWitnessPubKeyHash(pubHash, params)
220                 if err == nil {
221                         return address.EncodeAddress()
222                 }
223
224         case *btmConsensus.Params:
225                 params := netParams.(*btmConsensus.Params)
226                 address, err := btmCommon.NewAddressWitnessPubKeyHash(pubHash, params)
227                 if err == nil {
228                         return address.EncodeAddress()
229                 }
230         }
231         return ""
232 }
233
234 // BuildP2SHAddress create P2SH address
235 func BuildP2SHAddress(scriptHash []byte, netParams interface{}) string {
236         switch netParams.(type) {
237         case *consensus.Params:
238                 params := netParams.(*consensus.Params)
239                 address, err := common.NewAddressWitnessScriptHash(scriptHash, params)
240                 if err == nil {
241                         return address.EncodeAddress()
242                 }
243
244         case *btmConsensus.Params:
245                 params := netParams.(*btmConsensus.Params)
246                 address, err := btmCommon.NewAddressWitnessScriptHash(scriptHash, params)
247                 if err == nil {
248                         return address.EncodeAddress()
249                 }
250         }
251         return ""
252 }
253
254 // BuildAnnotatedOutput build the annotated output.
255 func (w *Wallet) BuildAnnotatedOutput(tx *types.Tx, idx int) *query.AnnotatedOutput {
256         orig := tx.Outputs[idx]
257         outid := tx.OutputID(idx)
258         out := &query.AnnotatedOutput{
259                 OutputID:        *outid,
260                 Position:        idx,
261                 AssetID:         *orig.AssetAmount().AssetId,
262                 AssetDefinition: &emptyJSONObject,
263                 Amount:          orig.AssetAmount().Amount,
264                 ControlProgram:  orig.ControlProgram(),
265         }
266
267         var isMainchainAddress bool
268         switch e := tx.Entries[*outid].(type) {
269         case *bc.IntraChainOutput:
270                 out.Type = "control"
271                 isMainchainAddress = false
272
273         case *bc.CrossChainOutput:
274                 out.Type = "cross_chain_out"
275                 isMainchainAddress = true
276
277         case *bc.VoteOutput:
278                 out.Type = "vote"
279                 out.Vote = e.Vote
280                 isMainchainAddress = false
281         }
282
283         out.Address = w.getAddressFromControlProgram(orig.ControlProgram(), isMainchainAddress)
284         return out
285 }