6 log "github.com/sirupsen/logrus"
8 "github.com/bytom/vapor/account"
9 "github.com/bytom/vapor/asset"
10 "github.com/bytom/vapor/blockchain/query"
11 "github.com/bytom/vapor/common"
12 "github.com/bytom/vapor/consensus"
13 "github.com/bytom/vapor/consensus/segwit"
14 "github.com/bytom/vapor/crypto/sha3pool"
15 "github.com/bytom/vapor/protocol/bc"
16 "github.com/bytom/vapor/protocol/bc/types"
19 // annotateTxs adds asset data to transactions
20 func annotateTxsAsset(w *Wallet, txs []*query.AnnotatedTx) {
21 for i, tx := range txs {
22 for j, input := range tx.Inputs {
23 alias, definition := w.getAliasDefinition(input.AssetID)
24 txs[i].Inputs[j].AssetAlias, txs[i].Inputs[j].AssetDefinition = alias, &definition
26 for k, output := range tx.Outputs {
27 alias, definition := w.getAliasDefinition(output.AssetID)
28 txs[i].Outputs[k].AssetAlias, txs[i].Outputs[k].AssetDefinition = alias, &definition
33 func (w *Wallet) getExternalDefinition(assetID *bc.AssetID) json.RawMessage {
34 externalAsset, err := w.Store.GetAsset(assetID)
35 if err != nil && err != ErrGetAsset {
36 log.WithFields(log.Fields{"module": logModule, "err": err, "assetID": assetID.String()}).Info("fail on get asset definition.")
38 if externalAsset == nil {
42 if err := w.AssetReg.SaveAsset(externalAsset, *externalAsset.Alias); err != nil {
43 log.WithFields(log.Fields{"module": logModule, "err": err, "assetAlias": *externalAsset.Alias}).Info("fail on save external asset to internal asset DB")
45 return json.RawMessage(externalAsset.RawDefinitionByte)
48 func (w *Wallet) getAliasDefinition(assetID bc.AssetID) (string, json.RawMessage) {
50 if assetID.String() == consensus.BTMAssetID.String() {
51 alias := consensus.BTMAlias
52 definition := []byte(asset.DefaultNativeAsset.RawDefinitionByte)
54 return alias, definition
57 //local asset and saved external asset
58 if localAsset, err := w.AssetReg.FindByID(nil, &assetID); err == nil {
59 alias := *localAsset.Alias
60 definition := []byte(localAsset.RawDefinitionByte)
61 return alias, definition
65 if definition := w.getExternalDefinition(&assetID); definition != nil {
66 return assetID.String(), definition
72 // annotateTxs adds account data to transactions
73 func (w *Wallet) annotateTxsAccount(txs []*query.AnnotatedTx) {
74 for i, tx := range txs {
75 for j, input := range tx.Inputs {
76 //issue asset tx input SpentOutputID is nil
77 if input.SpentOutputID == nil {
80 localAccount, err := w.getAccountFromACP(input.ControlProgram)
81 if localAccount == nil || err != nil {
84 txs[i].Inputs[j].AccountAlias = localAccount.Alias
85 txs[i].Inputs[j].AccountID = localAccount.ID
87 for j, output := range tx.Outputs {
88 localAccount, err := w.getAccountFromACP(output.ControlProgram)
89 if localAccount == nil || err != nil {
92 txs[i].Outputs[j].AccountAlias = localAccount.Alias
93 txs[i].Outputs[j].AccountID = localAccount.ID
98 func (w *Wallet) getAccountFromACP(program []byte) (*account.Account, error) {
100 sha3pool.Sum256(hash[:], program)
101 accountCP, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash))
106 account, err := w.AccountMgr.FindByID(accountCP.AccountID)
114 var emptyJSONObject = json.RawMessage(`{}`)
116 func (w *Wallet) buildAnnotatedTransaction(orig *types.Tx, b *types.Block, statusFail bool, indexInBlock int) *query.AnnotatedTx {
117 tx := &query.AnnotatedTx{
119 Timestamp: b.Timestamp,
121 BlockHeight: b.Height,
122 Position: uint32(indexInBlock),
123 BlockTransactionsCount: uint32(len(b.Transactions)),
124 Inputs: make([]*query.AnnotatedInput, 0, len(orig.Inputs)),
125 Outputs: make([]*query.AnnotatedOutput, 0, len(orig.Outputs)),
126 StatusFail: statusFail,
127 Size: orig.SerializedSize,
129 for i := range orig.Inputs {
130 tx.Inputs = append(tx.Inputs, w.BuildAnnotatedInput(orig, uint32(i)))
132 for i := range orig.Outputs {
133 tx.Outputs = append(tx.Outputs, w.BuildAnnotatedOutput(orig, i))
138 // BuildAnnotatedInput build the annotated input.
139 func (w *Wallet) BuildAnnotatedInput(tx *types.Tx, i uint32) *query.AnnotatedInput {
141 in := &query.AnnotatedInput{
142 AssetDefinition: &emptyJSONObject,
144 if orig.InputType() != types.CoinbaseInputType {
145 in.AssetID = orig.AssetID()
146 in.Amount = orig.Amount()
148 in.AssetID = *consensus.BTMAssetID
151 id := tx.Tx.InputIDs[i]
154 switch e := e.(type) {
157 in.ControlProgram = orig.ControlProgram()
158 in.Address = w.getAddressFromControlProgram(in.ControlProgram, false)
159 in.SpentOutputID = e.SpentOutputId
160 arguments := orig.Arguments()
161 for _, arg := range arguments {
162 in.WitnessArguments = append(in.WitnessArguments, arg)
165 case *bc.CrossChainInput:
166 in.Type = "cross_chain_in"
167 in.ControlProgram = orig.ControlProgram()
168 in.Address = w.getAddressFromControlProgram(in.ControlProgram, true)
169 in.SpentOutputID = e.MainchainOutputId
170 arguments := orig.Arguments()
171 for _, arg := range arguments {
172 in.WitnessArguments = append(in.WitnessArguments, arg)
177 in.ControlProgram = orig.ControlProgram()
178 in.Address = w.getAddressFromControlProgram(in.ControlProgram, false)
179 in.SpentOutputID = e.SpentOutputId
180 arguments := orig.Arguments()
181 for _, arg := range arguments {
182 in.WitnessArguments = append(in.WitnessArguments, arg)
187 in.Arbitrary = e.Arbitrary
192 func (w *Wallet) getAddressFromControlProgram(prog []byte, isMainchain bool) string {
193 netParams := &consensus.ActiveNetParams
195 netParams = consensus.BytomMainNetParams(&consensus.ActiveNetParams)
198 if segwit.IsP2WPKHScript(prog) {
199 if pubHash, err := segwit.GetHashFromStandardProg(prog); err == nil {
200 return BuildP2PKHAddress(pubHash, netParams)
202 } else if segwit.IsP2WSHScript(prog) {
203 if scriptHash, err := segwit.GetHashFromStandardProg(prog); err == nil {
204 return BuildP2SHAddress(scriptHash, netParams)
211 func BuildP2PKHAddress(pubHash []byte, netParams *consensus.Params) string {
212 address, err := common.NewAddressWitnessPubKeyHash(pubHash, netParams)
217 return address.EncodeAddress()
220 func BuildP2SHAddress(scriptHash []byte, netParams *consensus.Params) string {
221 address, err := common.NewAddressWitnessScriptHash(scriptHash, netParams)
226 return address.EncodeAddress()
229 // BuildAnnotatedOutput build the annotated output.
230 func (w *Wallet) BuildAnnotatedOutput(tx *types.Tx, idx int) *query.AnnotatedOutput {
231 orig := tx.Outputs[idx]
232 outid := tx.OutputID(idx)
233 out := &query.AnnotatedOutput{
236 AssetID: *orig.AssetAmount().AssetId,
237 AssetDefinition: &emptyJSONObject,
238 Amount: orig.AssetAmount().Amount,
239 ControlProgram: orig.ControlProgram(),
242 var isMainchainAddress bool
243 switch e := tx.Entries[*outid].(type) {
244 case *bc.IntraChainOutput:
246 isMainchainAddress = false
248 case *bc.CrossChainOutput:
249 out.Type = "cross_chain_out"
250 isMainchainAddress = true
255 isMainchainAddress = false
258 out.Address = w.getAddressFromControlProgram(orig.ControlProgram(), isMainchainAddress)