Peel the comparison to the original look at code 16: How does the/list-transactions show the transaction information?

Source: Internet
Author: User

Author: freewind

Compared to the original project warehouse:

GitHub Address: Https://github.com/Bytom/bytom

Gitee Address: Https://gitee.com/BytomBlockc ...

In the previous article, we tried to understand how it was traded, but because of the content, we divided it into a few minor issues and solved the "How to submit transaction information in Dashboard" in the previous article, as well as "How to do it" than the original background.

In this paper, we continue to study the next question: After the successful completion of the transaction submitted, the front end will display the transaction information in a list, how does it get the data in the background? That's how it's implemented:

Since it involves both front-end and back-end, we also divide it into two small problems:

    1. How does the frontend get the transaction data and display it?
    2. How does the backend find the transaction data?

The following is resolved in turn.

How does the frontend get the transaction data and display it?

Let's look at the original front-end code base. Since this feature is a "list page" display, it reminds me of a similar feature in front of a page showing the balance, where the src/features/shared/components/BaseList generic components are provided, so this should be the same here.

After looking, sure enough to src/features/transactions/components/ find the List.jsx file below, and the directory structure src/features/balances/components/ is very similar, and then a little bit of code, you can determine the use of the same method.

But if you think back, the process seems a little different. In the show balance there, we manually clicked the menu on the left-hand side bar, so that the front-end route was transferred to the /balances

Src/features/shared/routes.js#l5-l44

const makeRoutes = (store, type, List, New, Show, options = {}) => {  // ...  return {    path: options.path || type + 's',    component: RoutingContainer,    name: options.name || humanize(type + 's'),    name_zh: options.name_zh,    indexRoute: {      component: List,      onEnter: (nextState, replace) => {        loadPage(nextState, replace)      },      onChange: (_, nextState, replace) => { loadPage(nextState, replace) }    },    childRoutes: childRoutes  }}

onEnteror onChange triggered loadPage , the last step calls the background interface /list-balances . And this time in the example of this article, it is submitted the "Submit Transaction" form after the successful, automatically go to the "List Show Transactions" page, will not be the same trigger onEnter or not onChange ?

The answer is yes, because in the previous article, when submitForm executed, the last request to the background is /submit-transaction successful, and dealSignSubmitResp the function is called, and it is defined as:

src/features/transactions/actions.js#l120-l132

const dealSignSubmitResp = resp => {  // ...  dispatch(push({    pathname: '/transactions',    state: {      preserveFlash: true    }  }))}

As you can see, it will eventually switch the front-end route to the /transactions exact same route as the balance is displayed. So according to the experience over there, to the end will definitely visit the backend /list-transactions interface.

The derivation in this process is no longer detailed, you can see the previous explanation of "How to show the balance of the original" article.

Finally got the data returned in the background how to display in tabular form, also mentioned in the article, here also skipped.

How does the backend find the transaction data?

When we know that the front end will access the backend /list-transactions interface, it is easy to find the following code in the main project repository than the original:

api/api.go#l164-l244

func (a *API) buildHandler() {    // ...    if a.wallet != nil {        // ...        m.Handle("/list-transactions", jsonHandler(a.listTransactions))        // ...}

As you can see, list-transactions the corresponding handler are a.listTransactions :

api/query.go#l83-l107

func (a *API) listTransactions(ctx context.Context, filter struct {    ID        string `json:"id"`    AccountID string `json:"account_id"`    Detail    bool   `json:"detail"`}) Response {    transactions := []*query.AnnotatedTx{}    var err error    // 1.     if filter.AccountID != "" {        transactions, err = a.wallet.GetTransactionsByAccountID(filter.AccountID)    } else {        transactions, err = a.wallet.GetTransactionsByTxID(filter.ID)    }    // ...    // 2.    if filter.Detail == false {        txSummary := a.wallet.GetTransactionsSummary(transactions)        return NewSuccessResponse(txSummary)    }    return NewSuccessResponse(transactions)}

From the parameters of this method can be seen, the front end is can be passed over id , account_id and detail these three parameters. In the example of this article, because it is a direct jump to /transactions the route, so what parameters are not passed up.

I split the code into two pieces, and some of the error-handling parts were omitted from me. Explain in turn:

    1. The 1th point is to obtain according to the parameters transactions . If account_id there is a value, take it to fetch, that is, a transaction owned by an account, otherwise, id to fetch, this ID refers to the ID of the transaction. If neither of these are values, it should be handled in the second branch, that is, a.wallet.GetTransactionsByTxID you should also be able to handle cases where the argument is an empty string
    2. The 2nd code, if detail for false (if the front end does not pass value, it should be the default value false , then the previous get to the transactions summary, only return some information; otherwise, return the full information.

We advanced 1th in the code a.wallet.GetTransactionsByAccountID :

wallet/indexer.go#l505-l523

func (w *Wallet) GetTransactionsByAccountID(accountID string) ([]*query.AnnotatedTx, error) {    annotatedTxs := []*query.AnnotatedTx{}    // 1.    txIter := w.DB.IteratorPrefix([]byte(TxPrefix))    defer txIter.Release()    // 2.    for txIter.Next() {        // 3.        annotatedTx := &query.AnnotatedTx{}        if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {            return nil, err        }        // 4.        if findTransactionsByAccount(annotatedTx, accountID) {            annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})            annotatedTxs = append(annotatedTxs, annotatedTx)        }    }    return annotatedTxs, nil}

This divides the code into 4 pieces:

    1. The 1th code is to traverse the database to TxPrefix prefix the value, where TxPrefix the TXS: following is to be traversed. There's DB no doubt that wallet this leveldb
    2. Start the traversal at 2nd
    3. The 3rd place is to take out each element Value , which is in JSON format and turns it into an AnnotatedTx object. txIterEach element is a key-pair, yes, there is Key() , it's Value() only used here.Value()
    4. The 4th place is looking in the current annotatedTx object, if its input or output contains an account whose ID equals accountId , then it is what we need. The following method is used annotateTxsAsset to annotatedTx complement the asset related attributes (such as alias , etc.) in the object.

AnnotatedTxthe definition is worth a look:

Blockchain/query/annotated.go#l12-l22

type AnnotatedTx struct {    ID                     bc.Hash            `json:"tx_id"`    Timestamp              uint64             `json:"block_time"`    BlockID                bc.Hash            `json:"block_hash"`    BlockHeight            uint64             `json:"block_height"`    Position               uint32             `json:"block_index"`    BlockTransactionsCount uint32             `json:"block_transactions_count,omitempty"`    Inputs                 []*AnnotatedInput  `json:"inputs"`    Outputs                []*AnnotatedOutput `json:"outputs"`    StatusFail             bool               `json:"status_fail"`}

It is actually meant to hold the data that was last returned to the front end, by adding JSON-related convenience to each field to annotation convert to JSON.

If the front end has no account_id arguments, it enters another branch, corresponding to a.wallet.GetTransactionsByTxID(filter.ID) :

wallet/indexer.go#l426-l450

 func (w *wallet) Gettransactionsbytxid (TxID string) ([]*query. ANNOTATEDTX, error) {annotatedtxs: = []*query. annotatedtx{} Formatkey: = "" If TxID! = "" {rawformatkey: = w.db. Get (Calctxindexkey (TxID)) if Rawformatkey = = Nil {return nil, fmt. Errorf ("No Transaction (txid=%s)", Txid)} Formatkey = string (Rawformatkey)} txiter: = W.db. Iteratorprefix (Calcannotatedkey (Formatkey)) defer txiter.release () for Txiter.next () {annotatedtx: = &qu ery. annotatedtx{} If Err: = json. Unmarshal (Txiter.value (), ANNOTATEDTX); Err! = Nil {return nil, err} annotatetxsasset (w, []*query. ANNOTATEDTX{ANNOTATEDTX}) Annotatedtxs = append ([]*query.    ANNOTATEDTX{ANNOTATEDTX}, Annotatedtxs ...) } return Annotatedtxs, nil}  

This method looks very long, in fact the logic is relatively simple. If the front end is passed txID , the wallet transaction object of the specified id is searched, otherwise, all (that is, the case in this article) is taken. calcTxIndexKey(txID)the definition is:

Wallet/indexer.go#l61-l63

func calcTxIndexKey(txID string) []byte {    return []byte(TxIndexPrefix + txID)}

Which TxIndexPrefix is TID: .

calcAnnotatedKey(formatKey)is defined as:

Wallet/indexer.go#l53-l55

func calcAnnotatedKey(formatKey string) []byte {    return []byte(TxPrefix + formatKey)}

Where TxPrefix the value is TXS: .

We enter listTransactions the 2nd place again, that is detail there. If detail false so, only the digest is required, so it is called a.wallet.GetTransactionsSummary(transactions) :

wallet/indexer.go#l453-l486

Func (w *wallet) gettransactionssummary (transactions []*query. ANNOTATEDTX) []txsummary {Txs: = []txsummary{} for _, Annotatedtx: = Range Transactions {tmptxsummary: = Tx summary{Inputs:make ([]summary, Len (annotatedtx.inputs)), Outputs:make ([]summary, Len (annotat edtx.outputs)), ID:annotatedTx.ID, Timestamp:annotatedTx.Timestamp,} for I, Input: = range annotatedtx.inputs {tmptxsummary.inputs[i]. Type = input. Type Tmptxsummary.inputs[i]. AccountID = input. AccountID Tmptxsummary.inputs[i]. Accountalias = input. Accountalias Tmptxsummary.inputs[i]. AssetID = input. AssetID Tmptxsummary.inputs[i]. Assetalias = input. Assetalias Tmptxsummary.inputs[i]. Amount = input. Amount Tmptxsummary.inputs[i]. arbitrary = input. arbitrary} for J, output: = Range annotatedtx.outputs {tmptxsummary.outputs[j]. Type = output. Type            TMPTXSUMMARY.OUTPUTS[J]. AccountID = output. AccountID Tmptxsummary.outputs[j]. Accountalias = output. Accountalias Tmptxsummary.outputs[j]. AssetID = output. AssetID Tmptxsummary.outputs[j]. Assetalias = output. Assetalias Tmptxsummary.outputs[j]. Amount = output. Amount} Txs = Append (Txs, tmptxsummary)} return Txs}

The code in this section is fairly straightforward, which is to take transactions some of the more important information from the elements, make up the new TxSummary object, and return to the past. Finally, these objects become JSON back to the front end.

So today's small problem is solved, because of the previous experience can be used, so the feeling is relatively simple.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.