10 log "github.com/sirupsen/logrus"
12 "github.com/vapor/account"
13 "github.com/vapor/blockchain/query"
14 "github.com/vapor/consensus"
15 "github.com/vapor/crypto/sha3pool"
16 "github.com/vapor/database"
17 chainjson "github.com/vapor/encoding/json"
18 "github.com/vapor/protocol/bc"
19 "github.com/vapor/protocol/bc/types"
22 func parseGlobalTxIdx(globalTxIdx []byte) (*bc.Hash, uint64) {
23 var hashBytes [32]byte
24 copy(hashBytes[:], globalTxIdx[:32])
25 hash := bc.NewHash(hashBytes)
26 position := binary.BigEndian.Uint64(globalTxIdx[32:])
27 return &hash, position
30 // saveExternalAssetDefinition save external and local assets definition,
31 // when query ,query local first and if have no then query external
32 // details see getAliasDefinition
33 func saveExternalAssetDefinition(b *types.Block, store database.WalletStorer) {
35 defer store.CommitBatch()
37 for _, tx := range b.Transactions {
38 for _, orig := range tx.Inputs {
39 if cci, ok := orig.TypedInput.(*types.CrossChainInput); ok {
40 assetID := cci.AssetId
41 if assetExist := store.GetAssetDefinition(assetID); assetExist == nil {
42 store.SetAssetDefinition(assetID, cci.AssetDefinition)
49 // Summary is the struct of transaction's input and output summary
51 Type string `json:"type"`
52 AssetID bc.AssetID `json:"asset_id,omitempty"`
53 AssetAlias string `json:"asset_alias,omitempty"`
54 Amount uint64 `json:"amount,omitempty"`
55 AccountID string `json:"account_id,omitempty"`
56 AccountAlias string `json:"account_alias,omitempty"`
57 Arbitrary chainjson.HexBytes `json:"arbitrary,omitempty"`
60 // TxSummary is the struct of transaction summary
61 type TxSummary struct {
62 ID bc.Hash `json:"tx_id"`
63 Timestamp uint64 `json:"block_time"`
64 Inputs []Summary `json:"inputs"`
65 Outputs []Summary `json:"outputs"`
68 // indexTransactions saves all annotated transactions to the database.
69 func (w *Wallet) indexTransactions(b *types.Block, txStatus *bc.TransactionStatus, annotatedTxs []*query.AnnotatedTx) error {
70 for _, tx := range annotatedTxs {
71 rawTx, err := json.Marshal(tx)
73 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("inserting annotated_txs to db")
77 w.store.SetTransaction(b.Height, tx.Position, tx.ID.String(), rawTx)
78 w.store.DeleteUnconfirmedTransaction(tx.ID.String())
85 for position, globalTx := range b.Transactions {
86 blockHash := b.BlockHeader.Hash()
87 w.store.SetGlobalTransactionIndex(globalTx.ID.String(), &blockHash, uint64(position))
93 // filterAccountTxs related and build the fully annotated transactions.
94 func (w *Wallet) filterAccountTxs(b *types.Block, txStatus *bc.TransactionStatus) []*query.AnnotatedTx {
95 annotatedTxs := make([]*query.AnnotatedTx, 0, len(b.Transactions))
98 for pos, tx := range b.Transactions {
99 statusFail, _ := txStatus.GetStatus(pos)
100 for _, v := range tx.Outputs {
102 sha3pool.Sum256(hash[:], v.ControlProgram())
104 if bytes := w.store.GetRawProgram(hash); bytes != nil {
105 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
106 continue transactionLoop
110 for _, v := range tx.Inputs {
111 outid, err := v.SpentOutputID()
115 if bytes := w.store.GetStandardUTXO(outid); bytes != nil {
116 annotatedTxs = append(annotatedTxs, w.buildAnnotatedTransaction(tx, b, statusFail, pos))
117 continue transactionLoop
125 // GetTransactionByTxID get transaction by txID
126 func (w *Wallet) GetTransactionByTxID(txID string) (*query.AnnotatedTx, error) {
127 if annotatedTx, err := w.getAccountTxByTxID(txID); err == nil {
128 return annotatedTx, nil
129 } else if !w.TxIndexFlag {
133 return w.getGlobalTxByTxID(txID)
136 func (w *Wallet) getAccountTxByTxID(txID string) (*query.AnnotatedTx, error) {
137 annotatedTx := &query.AnnotatedTx{}
138 txInfo, err := w.store.GetTransaction(txID)
143 if err := json.Unmarshal(txInfo, annotatedTx); err != nil {
147 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
148 return annotatedTx, nil
151 func (w *Wallet) getGlobalTxByTxID(txID string) (*query.AnnotatedTx, error) {
152 globalTxIdx := w.store.GetGlobalTransaction(txID)
153 if globalTxIdx == nil {
154 return nil, fmt.Errorf("No transaction(tx_id=%s) ", txID)
157 blockHash, pos := parseGlobalTxIdx(globalTxIdx)
158 block, err := w.chain.GetBlockByHash(blockHash)
163 txStatus, err := w.chain.GetTransactionStatus(blockHash)
168 statusFail, err := txStatus.GetStatus(int(pos))
173 tx := block.Transactions[int(pos)]
174 return w.buildAnnotatedTransaction(tx, block, statusFail, int(pos)), nil
177 // GetTransactionsSummary get transactions summary
178 func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {
181 for _, annotatedTx := range transactions {
182 tmpTxSummary := TxSummary{
183 Inputs: make([]Summary, len(annotatedTx.Inputs)),
184 Outputs: make([]Summary, len(annotatedTx.Outputs)),
186 Timestamp: annotatedTx.Timestamp,
189 for i, input := range annotatedTx.Inputs {
190 tmpTxSummary.Inputs[i].Type = input.Type
191 tmpTxSummary.Inputs[i].AccountID = input.AccountID
192 tmpTxSummary.Inputs[i].AccountAlias = input.AccountAlias
193 tmpTxSummary.Inputs[i].AssetID = input.AssetID
194 tmpTxSummary.Inputs[i].AssetAlias = input.AssetAlias
195 tmpTxSummary.Inputs[i].Amount = input.Amount
196 tmpTxSummary.Inputs[i].Arbitrary = input.Arbitrary
198 for j, output := range annotatedTx.Outputs {
199 tmpTxSummary.Outputs[j].Type = output.Type
200 tmpTxSummary.Outputs[j].AccountID = output.AccountID
201 tmpTxSummary.Outputs[j].AccountAlias = output.AccountAlias
202 tmpTxSummary.Outputs[j].AssetID = output.AssetID
203 tmpTxSummary.Outputs[j].AssetAlias = output.AssetAlias
204 tmpTxSummary.Outputs[j].Amount = output.Amount
207 Txs = append(Txs, tmpTxSummary)
213 func findTransactionsByAccount(annotatedTx *query.AnnotatedTx, accountID string) bool {
214 for _, input := range annotatedTx.Inputs {
215 if input.AccountID == accountID {
220 for _, output := range annotatedTx.Outputs {
221 if output.AccountID == accountID {
229 // GetTransactions get all walletDB transactions, and filter transactions by accountID optional
230 func (w *Wallet) GetTransactions(accountID string) ([]*query.AnnotatedTx, error) {
231 annotatedTxs := []*query.AnnotatedTx{}
232 annotatedTxs, err := w.store.GetTransactions()
237 newAnnotatedTxs := []*query.AnnotatedTx{}
238 for _, annotatedTx := range annotatedTxs {
239 if accountID == "" || findTransactionsByAccount(annotatedTx, accountID) {
240 annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})
241 newAnnotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, newAnnotatedTxs...)
245 return newAnnotatedTxs, nil
248 // GetAccountBalances return all account balances
249 func (w *Wallet) GetAccountBalances(accountID string, id string) ([]AccountBalance, error) {
250 return w.indexBalances(w.GetAccountUtxos(accountID, "", false, false, false))
253 // AccountBalance account balance
254 type AccountBalance struct {
255 AccountID string `json:"account_id"`
256 Alias string `json:"account_alias"`
257 AssetAlias string `json:"asset_alias"`
258 AssetID string `json:"asset_id"`
259 Amount uint64 `json:"amount"`
260 AssetDefinition map[string]interface{} `json:"asset_definition"`
263 func (w *Wallet) indexBalances(accountUTXOs []*account.UTXO) ([]AccountBalance, error) {
264 accBalance := make(map[string]map[string]uint64)
265 balances := []AccountBalance{}
267 for _, accountUTXO := range accountUTXOs {
268 assetID := accountUTXO.AssetID.String()
269 if _, ok := accBalance[accountUTXO.AccountID]; ok {
270 if _, ok := accBalance[accountUTXO.AccountID][assetID]; ok {
271 accBalance[accountUTXO.AccountID][assetID] += accountUTXO.Amount
273 accBalance[accountUTXO.AccountID][assetID] = accountUTXO.Amount
276 accBalance[accountUTXO.AccountID] = map[string]uint64{assetID: accountUTXO.Amount}
280 var sortedAccount []string
281 for k := range accBalance {
282 sortedAccount = append(sortedAccount, k)
284 sort.Strings(sortedAccount)
286 for _, id := range sortedAccount {
287 var sortedAsset []string
288 for k := range accBalance[id] {
289 sortedAsset = append(sortedAsset, k)
291 sort.Strings(sortedAsset)
293 for _, assetID := range sortedAsset {
294 alias := w.AccountMgr.GetAliasByID(id)
295 targetAsset, err := w.AssetReg.GetAsset(assetID)
300 assetAlias := *targetAsset.Alias
301 balances = append(balances, AccountBalance{
305 AssetAlias: assetAlias,
306 Amount: accBalance[id][assetID],
307 AssetDefinition: targetAsset.DefinitionMap,
315 // GetAccountVotes return all account votes
316 func (w *Wallet) GetAccountVotes(accountID string, id string) ([]AccountVotes, error) {
317 return w.indexVotes(w.GetAccountUtxos(accountID, "", false, false, true))
320 type voteDetail struct {
321 Vote string `json:"vote"`
322 VoteNumber uint64 `json:"vote_number"`
325 // AccountVotes account vote
326 type AccountVotes struct {
327 AccountID string `json:"account_id"`
328 Alias string `json:"account_alias"`
329 TotalVoteNumber uint64 `json:"total_vote_number"`
330 VoteDetails []voteDetail `json:"vote_details"`
333 func (w *Wallet) indexVotes(accountUTXOs []*account.UTXO) ([]AccountVotes, error) {
334 accVote := make(map[string]map[string]uint64)
335 votes := []AccountVotes{}
337 for _, accountUTXO := range accountUTXOs {
338 if accountUTXO.AssetID != *consensus.BTMAssetID || accountUTXO.Vote == nil {
341 xpub := hex.EncodeToString(accountUTXO.Vote)
342 if _, ok := accVote[accountUTXO.AccountID]; ok {
343 accVote[accountUTXO.AccountID][xpub] += accountUTXO.Amount
345 accVote[accountUTXO.AccountID] = map[string]uint64{xpub: accountUTXO.Amount}
350 var sortedAccount []string
351 for k := range accVote {
352 sortedAccount = append(sortedAccount, k)
354 sort.Strings(sortedAccount)
356 for _, id := range sortedAccount {
357 var sortedXpub []string
358 for k := range accVote[id] {
359 sortedXpub = append(sortedXpub, k)
361 sort.Strings(sortedXpub)
363 voteDetails := []voteDetail{}
364 voteTotal := uint64(0)
365 for _, xpub := range sortedXpub {
366 voteDetails = append(voteDetails, voteDetail{
368 VoteNumber: accVote[id][xpub],
370 voteTotal += accVote[id][xpub]
372 alias := w.AccountMgr.GetAliasByID(id)
373 votes = append(votes, AccountVotes{
376 VoteDetails: voteDetails,
377 TotalVoteNumber: voteTotal,