9 log "github.com/sirupsen/logrus"
11 "github.com/vapor/account"
12 "github.com/vapor/blockchain/query"
13 "github.com/vapor/consensus"
14 "github.com/vapor/crypto/sha3pool"
15 chainjson "github.com/vapor/encoding/json"
16 "github.com/vapor/protocol/bc"
17 "github.com/vapor/protocol/bc/types"
20 func parseGlobalTxIdx(globalTxIdx []byte) (*bc.Hash, uint64) {
21 var hashBytes [32]byte
22 copy(hashBytes[:], globalTxIdx[:32])
23 hash := bc.NewHash(hashBytes)
24 position := binary.BigEndian.Uint64(globalTxIdx[32:])
25 return &hash, position
28 // saveExternalAssetDefinition save external and local assets definition,
29 // when query ,query local first and if have no then query external
30 // details see getAliasDefinition
31 func saveExternalAssetDefinition(b *types.Block, store WalletStore) error {
32 newStore := store.InitBatch()
34 for _, tx := range b.Transactions {
35 for _, orig := range tx.Inputs {
36 if cci, ok := orig.TypedInput.(*types.CrossChainInput); ok {
37 assetID := cci.AssetId
38 if _, err := newStore.GetAsset(assetID); err == nil {
40 } else if err != ErrGetAsset {
44 newStore.SetAssetDefinition(assetID, cci.AssetDefinition)
48 if err := newStore.CommitBatch(); err != nil {
55 // Summary is the struct of transaction's input and output summary
57 Type string `json:"type"`
58 AssetID bc.AssetID `json:"asset_id,omitempty"`
59 AssetAlias string `json:"asset_alias,omitempty"`
60 Amount uint64 `json:"amount,omitempty"`
61 AccountID string `json:"account_id,omitempty"`
62 AccountAlias string `json:"account_alias,omitempty"`
63 Arbitrary chainjson.HexBytes `json:"arbitrary,omitempty"`
66 // TxSummary is the struct of transaction summary
67 type TxSummary struct {
68 ID bc.Hash `json:"tx_id"`
69 Timestamp uint64 `json:"block_time"`
70 Inputs []Summary `json:"inputs"`
71 Outputs []Summary `json:"outputs"`
74 // indexTransactions saves all annotated transactions to the database.
75 func (w *Wallet) indexTransactions(b *types.Block, txStatus *bc.TransactionStatus, annotatedTxs []*query.AnnotatedTx, store WalletStore) error {
76 for _, tx := range annotatedTxs {
77 if err := w.Store.SetTransaction(b.Height, tx); err != nil {
81 store.DeleteUnconfirmedTransaction(tx.ID.String())
88 for position, globalTx := range b.Transactions {
89 blockHash := b.BlockHeader.Hash()
90 store.SetGlobalTransactionIndex(globalTx.ID.String(), &blockHash, uint64(position))
96 // filterAccountTxs related and build the fully annotated transactions.
97 func (w *Wallet) filterAccountTxs(b *types.Block, txStatus *bc.TransactionStatus) []*query.AnnotatedTx {
98 annotatedTxs := make([]*query.AnnotatedTx, 0, len(b.Transactions))
101 for pos, tx := range b.Transactions {
102 statusFail, _ := txStatus.GetStatus(pos)
103 for _, v := range tx.Outputs {
105 sha3pool.Sum256(hash[:], v.ControlProgram())
106 if _, err := w.AccountMgr.GetControlProgram(bc.NewHash(hash)); err == nil {
107 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
108 continue transactionLoop
110 log.WithFields(log.Fields{"module": logModule, "err": err, "hash": hex.EncodeToString(hash[:])}).Info("filterAccountTxs fail.")
114 for _, v := range tx.Inputs {
115 outid, err := v.SpentOutputID()
117 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": hex.EncodeToString(outid.Bytes())}).Info("filterAccountTxs fail.")
120 if _, err = w.Store.GetStandardUTXO(outid); err == nil {
121 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
122 continue transactionLoop
124 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": hex.EncodeToString(outid.Bytes())}).Info("filterAccountTxs fail.")
132 // GetTransactionByTxID get transaction by txID
133 func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
134 if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil {
135 return annotatedTx, nil
136 } else if !w.TxIndexFlag {
140 return w.getGlobalTxByTxID(txID)
143 func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
144 annotatedTx, err := w.Store.GetTransaction(txID)
149 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
150 return annotatedTx, nil
153 func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) {
154 globalTxIdx := w.Store.GetGlobalTransactionIndex(txID)
155 if globalTxIdx == nil {
156 return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
159 blockHash, pos := parseGlobalTxIdx(globalTxIdx)
160 block, err := w.Chain.GetBlockByHash(blockHash)
165 txStatus, err := w.Chain.GetTransactionStatus(blockHash)
170 statusFail, err := txStatus.GetStatus(int(pos))
175 tx := block.Transactions[int(pos)]
176 return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil
179 // GetTransactionsSummary get transactions summary
180 func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {
183 for _, annotatedTx := range transactions {
184 tmpTxSummary := TxSummary{
185 Inputs: make([]Summary, len(annotatedTx.Inputs)),
186 Outputs: make([]Summary, len(annotatedTx.Outputs)),
188 Timestamp: annotatedTx.Timestamp,
191 for i, input := range annotatedTx.Inputs {
192 tmpTxSummary.Inputs[i].Type = input.Type
193 tmpTxSummary.Inputs[i].AccountID = input.AccountID
194 tmpTxSummary.Inputs[i].AccountAlias = input.AccountAlias
195 tmpTxSummary.Inputs[i].AssetID = input.AssetID
196 tmpTxSummary.Inputs[i].AssetAlias = input.AssetAlias
197 tmpTxSummary.Inputs[i].Amount = input.Amount
198 tmpTxSummary.Inputs[i].Arbitrary = input.Arbitrary
200 for j, output := range annotatedTx.Outputs {
201 tmpTxSummary.Outputs[j].Type = output.Type
202 tmpTxSummary.Outputs[j].AccountID = output.AccountID
203 tmpTxSummary.Outputs[j].AccountAlias = output.AccountAlias
204 tmpTxSummary.Outputs[j].AssetID = output.AssetID
205 tmpTxSummary.Outputs[j].AssetAlias = output.AssetAlias
206 tmpTxSummary.Outputs[j].Amount = output.Amount
209 Txs = append(Txs, tmpTxSummary)
215 func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string) bool {
216 for _, input := range annotatedTx.Inputs {
217 if input.AccountID == accountID {
222 for _, output := range annotatedTx.Outputs {
223 if output.AccountID == accountID {
231 // GetTransactions get all walletDB transactions or unconfirmed transactions, and filter transactions by accountID and StartTxID optional
232 func (w *Wallet) GetTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
233 annotatedTxs := []*query.AnnotatedTx{}
234 annotatedTxs, err := w.Store.ListTransactions(accountID, StartTxID, count, unconfirmed)
239 newAnnotatedTxs := []*query.AnnotatedTx{}
240 for _, annotatedTx := range annotatedTxs {
241 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
242 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
243 newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
248 sort.Sort(SortByTimestamp(annotatedTxs))
250 sort.Sort(SortByHeight(annotatedTxs))
253 return newAnnotatedTxs, nil
256 // GetAccountBalances return all account balances
257 func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) {
258 return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false, false))
261 // AccountBalance account balance
262 type AccountBalance struct {
263 AccountID string `json:"account_id"`
264 Alias string `json:"account_alias"`
265 AssetAlias string `json:"asset_alias"`
266 AssetID string `json:"asset_id"`
267 Amount uint64 `json:"amount"`
268 AssetDefinition map[string]interface{} `json:"asset_definition"`
271 func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, error) {
272 accBalance := make(map[string]map[string]uint64)
273 balances := []AccountBalance{}
275 for _, accountUTXO := range accountUTXOs {
276 assetID := accountUTXO.AssetID.String()
277 if _, ok := accBalance[accountUTXO.AccountID]; ok {
278 if _, ok := accBalance[accountUTXO.AccountID][assetID]; ok {
279 accBalance[accountUTXO.AccountID][assetID] += accountUTXO.Amount
281 accBalance[accountUTXO.AccountID][assetID] = accountUTXO.Amount
284 accBalance[accountUTXO.AccountID] = map[string]uint64{assetID: accountUTXO.Amount}
288 var sortedAccount []string
289 for k := range accBalance {
290 sortedAccount = append(sortedAccount, k)
292 sort.Strings(sortedAccount)
294 for _, id := range sortedAccount {
295 var sortedAsset []string
296 for k := range accBalance[id] {
297 sortedAsset = append(sortedAsset, k)
299 sort.Strings(sortedAsset)
301 for _, assetID := range sortedAsset {
302 alias := w.AccountMgr.GetAliasByID(id)
303 targetAsset, err := w.AssetReg.GetAsset(assetID)
308 assetAlias := *targetAsset.Alias
309 balances = append(balances, AccountBalance{
313 AssetAlias: assetAlias,
314 Amount: accBalance[id][assetID],
315 AssetDefinition: targetAsset.DefinitionMap,
323 // GetAccountVotes return all account votes
324 func (w *Wallet) GetAccountVotes(accountID string, id string) ([]AccountVotes, error) {
325 return w.indexVotes(w.GetAccountUtxos(accountID, "", false, false, true))
328 type voteDetail struct {
329 Vote string `json:"vote"`
330 VoteNumber uint64 `json:"vote_number"`
333 // AccountVotes account vote
334 type AccountVotes struct {
335 AccountID string `json:"account_id"`
336 Alias string `json:"account_alias"`
337 TotalVoteNumber uint64 `json:"total_vote_number"`
338 VoteDetails []voteDetail `json:"vote_details"`
341 func (w *Wallet) indexVotes(accountUTXOs []*account.UTXO) ([]AccountVotes, error) {
342 accVote := make(map[string]map[string]uint64)
343 votes := []AccountVotes{}
345 for _, accountUTXO := range accountUTXOs {
346 if accountUTXO.AssetID != *consensus.BTMAssetID || accountUTXO.Vote == nil {
349 xpub := hex.EncodeToString(accountUTXO.Vote)
350 if _, ok := accVote[accountUTXO.AccountID]; ok {
351 accVote[accountUTXO.AccountID][xpub] += accountUTXO.Amount
353 accVote[accountUTXO.AccountID] = map[string]uint64{xpub: accountUTXO.Amount}
358 var sortedAccount []string
359 for k := range accVote {
360 sortedAccount = append(sortedAccount, k)
362 sort.Strings(sortedAccount)
364 for _, id := range sortedAccount {
365 var sortedXpub []string
366 for k := range accVote[id] {
367 sortedXpub = append(sortedXpub, k)
369 sort.Strings(sortedXpub)
371 voteDetails := []voteDetail{}
372 voteTotal := uint64(0)
373 for _, xpub := range sortedXpub {
374 voteDetails = append(voteDetails, voteDetail{
376 VoteNumber: accVote[id][xpub],
378 voteTotal += accVote[id][xpub]
380 alias := w.AccountMgr.GetAliasByID(id)
381 votes = append(votes, AccountVotes{
384 VoteDetails: voteDetails,
385 TotalVoteNumber: voteTotal,