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 cp, err := w.store.GetControlProgram(bc.NewHash(hash))
110 log.WithFields(log.Fields{"module": logModule, "err": err, "hash": string(hash[:])}).Error("filterAccountTxs fail.")
114 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
115 continue transactionLoop
119 for _, v := range tx.Inputs {
120 outid, err := v.SpentOutputID()
122 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
125 utxo, err := w.store.GetStandardUTXO(outid)
127 log.WithFields(log.Fields{"module": logModule, "err": err, "outputID": outid.String()}).Error("filterAccountTxs fail.")
131 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
132 continue transactionLoop
140 // GetTransactionByTxID get transaction by txID
141 func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
142 if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil {
143 return annotatedTx, nil
144 } else if !w.TxIndexFlag {
148 return w.getGlobalTxByTxID(txID)
151 func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
152 annotatedTx, err := w.store.GetTransaction(txID)
156 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
157 return annotatedTx, nil
160 func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) {
161 globalTxIdx := w.store.GetGlobalTransactionIndex(txID)
162 if globalTxIdx == nil {
163 return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
166 blockHash, pos := parseGlobalTxIdx(globalTxIdx)
167 block, err := w.chain.GetBlockByHash(blockHash)
172 txStatus, err := w.chain.GetTransactionStatus(blockHash)
177 statusFail, err := txStatus.GetStatus(int(pos))
182 tx := block.Transactions[int(pos)]
183 return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil
186 // GetTransactionsSummary get transactions summary
187 func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {
190 for _, annotatedTx := range transactions {
191 tmpTxSummary := TxSummary{
192 Inputs: make([]Summary, len(annotatedTx.Inputs)),
193 Outputs: make([]Summary, len(annotatedTx.Outputs)),
195 Timestamp: annotatedTx.Timestamp,
198 for i, input := range annotatedTx.Inputs {
199 tmpTxSummary.Inputs[i].Type = input.Type
200 tmpTxSummary.Inputs[i].AccountID = input.AccountID
201 tmpTxSummary.Inputs[i].AccountAlias = input.AccountAlias
202 tmpTxSummary.Inputs[i].AssetID = input.AssetID
203 tmpTxSummary.Inputs[i].AssetAlias = input.AssetAlias
204 tmpTxSummary.Inputs[i].Amount = input.Amount
205 tmpTxSummary.Inputs[i].Arbitrary = input.Arbitrary
207 for j, output := range annotatedTx.Outputs {
208 tmpTxSummary.Outputs[j].Type = output.Type
209 tmpTxSummary.Outputs[j].AccountID = output.AccountID
210 tmpTxSummary.Outputs[j].AccountAlias = output.AccountAlias
211 tmpTxSummary.Outputs[j].AssetID = output.AssetID
212 tmpTxSummary.Outputs[j].AssetAlias = output.AssetAlias
213 tmpTxSummary.Outputs[j].Amount = output.Amount
216 Txs = append(Txs, tmpTxSummary)
222 func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string) bool {
223 for _, input := range annotatedTx.Inputs {
224 if input.AccountID == accountID {
229 for _, output := range annotatedTx.Outputs {
230 if output.AccountID == accountID {
238 // GetTransactions get all walletDB transactions or unconfirmed transactions, and filter transactions by accountID and StartTxID optional
239 func (w *Wallet) GetTransactions(accountID string, StartTxID string, count uint, unconfirmed bool) ([]*query.AnnotatedTx, error) {
240 annotatedTxs := []*query.AnnotatedTx{}
241 annotatedTxs, err := w.store.ListTransactions(accountID, StartTxID, count, unconfirmed)
246 newAnnotatedTxs := []*query.AnnotatedTx{}
247 for _, annotatedTx := range annotatedTxs {
248 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
249 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
250 newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
255 sort.Sort(SortByTimestamp(annotatedTxs))
258 return newAnnotatedTxs, nil
261 // GetAccountBalances return all account balances
262 func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) {
263 return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false, false))
266 // AccountBalance account balance
267 type AccountBalance struct {
268 AccountID string `json:"account_id"`
269 Alias string `json:"account_alias"`
270 AssetAlias string `json:"asset_alias"`
271 AssetID string `json:"asset_id"`
272 Amount uint64 `json:"amount"`
273 AssetDefinition map[string]interface{} `json:"asset_definition"`
276 func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, error) {
277 accBalance := make(map[string]map[string]uint64)
278 balances := []AccountBalance{}
280 for _, accountUTXO := range accountUTXOs {
281 assetID := accountUTXO.AssetID.String()
282 if _, ok := accBalance[accountUTXO.AccountID]; ok {
283 if _, ok := accBalance[accountUTXO.AccountID][assetID]; ok {
284 accBalance[accountUTXO.AccountID][assetID] += accountUTXO.Amount
286 accBalance[accountUTXO.AccountID][assetID] = accountUTXO.Amount
289 accBalance[accountUTXO.AccountID] = map[string]uint64{assetID: accountUTXO.Amount}
293 var sortedAccount []string
294 for k := range accBalance {
295 sortedAccount = append(sortedAccount, k)
297 sort.Strings(sortedAccount)
299 for _, id := range sortedAccount {
300 var sortedAsset []string
301 for k := range accBalance[id] {
302 sortedAsset = append(sortedAsset, k)
304 sort.Strings(sortedAsset)
306 for _, assetID := range sortedAsset {
307 alias := w.AccountMgr.GetAliasByID(id)
308 targetAsset, err := w.AssetReg.GetAsset(assetID)
313 assetAlias := *targetAsset.Alias
314 balances = append(balances, AccountBalance{
318 AssetAlias: assetAlias,
319 Amount: accBalance[id][assetID],
320 AssetDefinition: targetAsset.DefinitionMap,
328 // GetAccountVotes return all account votes
329 func (w *Wallet) GetAccountVotes(accountID string, id string) ([]AccountVotes, error) {
330 return w.indexVotes(w.GetAccountUtxos(accountID, "", false, false, true))
333 type voteDetail struct {
334 Vote string `json:"vote"`
335 VoteNumber uint64 `json:"vote_number"`
338 // AccountVotes account vote
339 type AccountVotes struct {
340 AccountID string `json:"account_id"`
341 Alias string `json:"account_alias"`
342 TotalVoteNumber uint64 `json:"total_vote_number"`
343 VoteDetails []voteDetail `json:"vote_details"`
346 func (w *Wallet) indexVotes(accountUTXOs []*account.UTXO) ([]AccountVotes, error) {
347 accVote := make(map[string]map[string]uint64)
348 votes := []AccountVotes{}
350 for _, accountUTXO := range accountUTXOs {
351 if accountUTXO.AssetID != *consensus.BTMAssetID || accountUTXO.Vote == nil {
354 xpub := hex.EncodeToString(accountUTXO.Vote)
355 if _, ok := accVote[accountUTXO.AccountID]; ok {
356 accVote[accountUTXO.AccountID][xpub] += accountUTXO.Amount
358 accVote[accountUTXO.AccountID] = map[string]uint64{xpub: accountUTXO.Amount}
363 var sortedAccount []string
364 for k := range accVote {
365 sortedAccount = append(sortedAccount, k)
367 sort.Strings(sortedAccount)
369 for _, id := range sortedAccount {
370 var sortedXpub []string
371 for k := range accVote[id] {
372 sortedXpub = append(sortedXpub, k)
374 sort.Strings(sortedXpub)
376 voteDetails := []voteDetail{}
377 voteTotal := uint64(0)
378 for _, xpub := range sortedXpub {
379 voteDetails = append(voteDetails, voteDetail{
381 VoteNumber: accVote[id][xpub],
383 voteTotal += accVote[id][xpub]
385 alias := w.AccountMgr.GetAliasByID(id)
386 votes = append(votes, AccountVotes{
389 VoteDetails: voteDetails,
390 TotalVoteNumber: voteTotal,