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 WalletStorer) error {
32 if err := store.InitBatch(); err != nil {
36 for _, tx := range b.Transactions {
37 for _, orig := range tx.Inputs {
38 if cci, ok := orig.TypedInput.(*types.CrossChainInput); ok {
39 assetID := cci.AssetId
40 assetExist, err := store.GetAsset(assetID)
44 if assetExist == nil {
45 store.SetAssetDefinition(assetID, cci.AssetDefinition)
51 if err := store.CommitBatch(); err != nil {
57 // Summary is the struct of transaction's input and output summary
59 Type string `json:"type"`
60 AssetID bc.AssetID `json:"asset_id,omitempty"`
61 AssetAlias string `json:"asset_alias,omitempty"`
62 Amount uint64 `json:"amount,omitempty"`
63 AccountID string `json:"account_id,omitempty"`
64 AccountAlias string `json:"account_alias,omitempty"`
65 Arbitrary chainjson.HexBytes `json:"arbitrary,omitempty"`
68 // TxSummary is the struct of transaction summary
69 type TxSummary struct {
70 ID bc.Hash `json:"tx_id"`
71 Timestamp uint64 `json:"block_time"`
72 Inputs []Summary `json:"inputs"`
73 Outputs []Summary `json:"outputs"`
76 // indexTransactions saves all annotated transactions to the database.
77 func (w *Wallet) indexTransactions(b *types.Block, txStatus *bc.TransactionStatus, annotatedTxs []*query.AnnotatedTx) error {
78 for _, tx := range annotatedTxs {
79 if err := w.store.SetTransaction(b.Height, tx); err != nil {
82 w.store.DeleteUnconfirmedTransaction(tx.ID.String())
89 for position, globalTx := range b.Transactions {
90 blockHash := b.BlockHeader.Hash()
91 w.store.SetGlobalTransactionIndex(globalTx.ID.String(), &blockHash, uint64(position))
97 // filterAccountTxs related and build the fully annotated transactions.
98 func (w *Wallet) filterAccountTxs(b *types.Block, txStatus *bc.TransactionStatus) []*query.AnnotatedTx {
99 annotatedTxs := make([]*query.AnnotatedTx, 0, len(b.Transactions))
102 for pos, tx := range b.Transactions {
103 statusFail, _ := txStatus.GetStatus(pos)
104 for _, v := range tx.Outputs {
106 sha3pool.Sum256(hash[:], v.ControlProgram())
108 _, err := w.store.GetControlProgram(bc.NewHash(hash))
110 log.WithFields(log.Fields{"module": logModule, "err": err, "hash": string(hash[:])}).Error("filterAccountTxs fail.")
113 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
114 continue transactionLoop
117 for _, v := range tx.Inputs {
118 outid, err := v.SpentOutputID()
120 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
123 _, err = w.store.GetStandardUTXO(outid)
125 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
128 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
129 continue transactionLoop
136 // GetTransactionByTxID get transaction by txID
137 func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
138 if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil {
139 return annotatedTx, nil
140 } else if !w.TxIndexFlag {
144 return w.getGlobalTxByTxID(txID)
147 func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
148 annotatedTx, err := w.store.GetTransaction(txID)
152 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
153 return annotatedTx, nil
156 func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) {
157 globalTxIdx := w.store.GetGlobalTransactionIndex(txID)
158 if globalTxIdx == nil {
159 return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
162 blockHash, pos := parseGlobalTxIdx(globalTxIdx)
163 block, err := w.chain.GetBlockByHash(blockHash)
168 txStatus, err := w.chain.GetTransactionStatus(blockHash)
173 statusFail, err := txStatus.GetStatus(int(pos))
178 tx := block.Transactions[int(pos)]
179 return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil
182 // GetTransactionsSummary get transactions summary
183 func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {
186 for _, annotatedTx := range transactions {
187 tmpTxSummary := TxSummary{
188 Inputs: make([]Summary, len(annotatedTx.Inputs)),
189 Outputs: make([]Summary, len(annotatedTx.Outputs)),
191 Timestamp: annotatedTx.Timestamp,
194 for i, input := range annotatedTx.Inputs {
195 tmpTxSummary.Inputs[i].Type = input.Type
196 tmpTxSummary.Inputs[i].AccountID = input.AccountID
197 tmpTxSummary.Inputs[i].AccountAlias = input.AccountAlias
198 tmpTxSummary.Inputs[i].AssetID = input.AssetID
199 tmpTxSummary.Inputs[i].AssetAlias = input.AssetAlias
200 tmpTxSummary.Inputs[i].Amount = input.Amount
201 tmpTxSummary.Inputs[i].Arbitrary = input.Arbitrary
203 for j, output := range annotatedTx.Outputs {
204 tmpTxSummary.Outputs[j].Type = output.Type
205 tmpTxSummary.Outputs[j].AccountID = output.AccountID
206 tmpTxSummary.Outputs[j].AccountAlias = output.AccountAlias
207 tmpTxSummary.Outputs[j].AssetID = output.AssetID
208 tmpTxSummary.Outputs[j].AssetAlias = output.AssetAlias
209 tmpTxSummary.Outputs[j].Amount = output.Amount
212 Txs = append(Txs, tmpTxSummary)
218 func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string) bool {
219 for _, input := range annotatedTx.Inputs {
220 if input.AccountID == accountID {
225 for _, output := range annotatedTx.Outputs {
226 if output.AccountID == accountID {
234 // GetTransactions get all walletDB transactions or unconfirmed transactions, and filter transactions by accountID and StartTxID optional
235 func (w *Wallet) GetTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
236 annotatedTxs := []*query.AnnotatedTx{}
237 annotatedTxs, err := w.store.ListTransactions(accountID, StartTxID, count, unconfirmed)
242 newAnnotatedTxs := []*query.AnnotatedTx{}
243 for _, annotatedTx := range annotatedTxs {
244 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
245 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
246 newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
251 sort.Sort(SortByTimestamp(annotatedTxs))
253 sort.Sort(SortByHeight(annotatedTxs))
256 return newAnnotatedTxs, nil
259 // GetAccountBalances return all account balances
260 func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) {
261 return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false, false))
264 // AccountBalance account balance
265 type AccountBalance struct {
266 AccountID string `json:"account_id"`
267 Alias string `json:"account_alias"`
268 AssetAlias string `json:"asset_alias"`
269 AssetID string `json:"asset_id"`
270 Amount uint64 `json:"amount"`
271 AssetDefinition map[string]interface{} `json:"asset_definition"`
274 func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, error) {
275 accBalance := make(map[string]map[string]uint64)
276 balances := []AccountBalance{}
278 for _, accountUTXO := range accountUTXOs {
279 assetID := accountUTXO.AssetID.String()
280 if _, ok := accBalance[accountUTXO.AccountID]; ok {
281 if _, ok := accBalance[accountUTXO.AccountID][assetID]; ok {
282 accBalance[accountUTXO.AccountID][assetID] += accountUTXO.Amount
284 accBalance[accountUTXO.AccountID][assetID] = accountUTXO.Amount
287 accBalance[accountUTXO.AccountID] = map[string]uint64{assetID: accountUTXO.Amount}
291 var sortedAccount []string
292 for k := range accBalance {
293 sortedAccount = append(sortedAccount, k)
295 sort.Strings(sortedAccount)
297 for _, id := range sortedAccount {
298 var sortedAsset []string
299 for k := range accBalance[id] {
300 sortedAsset = append(sortedAsset, k)
302 sort.Strings(sortedAsset)
304 for _, assetID := range sortedAsset {
305 alias := w.AccountMgr.GetAliasByID(id)
306 targetAsset, err := w.AssetReg.GetAsset(assetID)
311 assetAlias := *targetAsset.Alias
312 balances = append(balances, AccountBalance{
316 AssetAlias: assetAlias,
317 Amount: accBalance[id][assetID],
318 AssetDefinition: targetAsset.DefinitionMap,
326 // GetAccountVotes return all account votes
327 func (w *Wallet) GetAccountVotes(accountID string, id string) ([]AccountVotes, error) {
328 return w.indexVotes(w.GetAccountUtxos(accountID, "", false, false, true))
331 type voteDetail struct {
332 Vote string `json:"vote"`
333 VoteNumber uint64 `json:"vote_number"`
336 // AccountVotes account vote
337 type AccountVotes struct {
338 AccountID string `json:"account_id"`
339 Alias string `json:"account_alias"`
340 TotalVoteNumber uint64 `json:"total_vote_number"`
341 VoteDetails []voteDetail `json:"vote_details"`
344 func (w *Wallet) indexVotes(accountUTXOs []*account.UTXO) ([]AccountVotes, error) {
345 accVote := make(map[string]map[string]uint64)
346 votes := []AccountVotes{}
348 for _, accountUTXO := range accountUTXOs {
349 if accountUTXO.AssetID != *consensus.BTMAssetID || accountUTXO.Vote == nil {
352 xpub := hex.EncodeToString(accountUTXO.Vote)
353 if _, ok := accVote[accountUTXO.AccountID]; ok {
354 accVote[accountUTXO.AccountID][xpub] += accountUTXO.Amount
356 accVote[accountUTXO.AccountID] = map[string]uint64{xpub: accountUTXO.Amount}
361 var sortedAccount []string
362 for k := range accVote {
363 sortedAccount = append(sortedAccount, k)
365 sort.Strings(sortedAccount)
367 for _, id := range sortedAccount {
368 var sortedXpub []string
369 for k := range accVote[id] {
370 sortedXpub = append(sortedXpub, k)
372 sort.Strings(sortedXpub)
374 voteDetails := []voteDetail{}
375 voteTotal := uint64(0)
376 for _, xpub := range sortedXpub {
377 voteDetails = append(voteDetails, voteDetail{
379 VoteNumber: accVote[id][xpub],
381 voteTotal += accVote[id][xpub]
383 alias := w.AccountMgr.GetAliasByID(id)
384 votes = append(votes, AccountVotes{
387 VoteDetails: voteDetails,
388 TotalVoteNumber: voteTotal,