国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

剝開比原看代碼15:比原是如何轉帳的

Tony / 1833人閱讀

摘要:圖中,轉帳表單是如何把轉帳數據提交到后臺的由于是前端,所以我們要去從前端的代碼庫中尋找。繼續看這段代碼內容還是比較多的,但總體基本上還是對參數進行驗證補全和轉換,然后交給后面的方法處理。

作者:freewind

比原項目倉庫:

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockc...

在前面幾篇中,我們做了足夠了準備,現在終于可以試一試轉帳功能了!

這里的轉帳最好使用solonet,再按前一篇文章的辦法修改代碼后產生單機測試幣,然后再試。在此之前,如果需要的話,請先備份好你之前的帳戶,然后刪除(或重命名)你的數據目錄,再使用bytomd init --chain_id=solonet重新初始化。

下面是我通過dashboard進行的轉帳操作,在操作之前,我先建立了兩個帳戶,然后把錢從一個帳戶轉到另一個帳戶的地址中:

新建一個交易,填上把哪個帳戶的哪種資產轉到某個地址上。可以看到還要消耗一定的gas:

(上圖為圖1)

轉帳成功后,如下:

(上圖為圖2)

我們看一下這個交易的詳細信息,由于太長,截成了兩個圖:


(上面兩圖合稱為圖3)

我們今天(以及往后的幾天)就是把這一塊流程搞清楚。

由于上面展示的操作還是有點多的,所以我們還是按之前的套路,先把它分解成多個小問題,一一解決:

圖1中,轉帳界面是如何把轉帳數據提交到后臺的?

圖1中,后臺是如何接收到轉帳數據并執行轉帳操作的?

圖2中,前臺是如何拿到后臺的數據并展示出來的?

圖3中,前臺是如何拿到后臺的數據并展示出來的?

今天的文章,我們主要是研究前兩個問題,即跟圖1相關的邏輯。

圖1中,轉帳表單是如何把轉帳數據提交到后臺的?

由于是前端,所以我們要去從前端的代碼庫中尋找。通過搜索“簡單交易”這個詞,我們很快定位到下面這塊代碼:

src/features/transactions/components/New/New.jsx#L275-L480

    return (
      
      // ...
      
    )

由于上面的代碼實在太長太細節,全是一些jsx用于生成表單的代碼,我們就跳過算了,有興趣的同學可以自行看細節。我們需要關注的是,當我們單擊了“提交交易”的按鈕以后,this.submitWithValidation會被調用,而它對應的代碼是:

src/features/transactions/components/New/New.jsx#L159-L177

  submitWithValidation(data) {
    return new Promise((resolve, reject) => {
      this.props.submitForm(Object.assign({}, data, {state: this.state}))
        .catch((err) => {
          // ...
          return reject(response)
        })
    })
  }

通常我們應該會在這個函數里找到一些線索,發現數據會提交到后臺哪個接口。但是這次卻好像沒有有用的信息,只有一個來自于props的看起來非常通用的submitForm??磥硇枰嗾艺揖€索。

好在很快在同一個文件的最后面,看到了用于把React組件與Redux連接起來的代碼,非常有用:

src/features/transactions/components/New/New.jsx#L515-L572

export default BaseNew.connect(
  (state) => {
    // ...
    return {
      // ...
    }
  },
  (dispatch) => ({
    // ...
    ...BaseNew.mapDispatchToProps("transaction")(dispatch)
  }),
  // ...
  )(Form)
)

我把不太關注的內容都省略了,需要關注的是BaseNew.mapDispatchToProps("transaction")(dispatch)這一行。

為什么要關注mapDispatchToProps這個方法呢?這是因為當我們點擊了表單中的提交按鈕后,不論中間怎么操作,最后一定要調用dispatch來處理某個action。而在前面看到,點擊“提交交易”后,執行的是this.props.submitForm,通過this.props.可以看出,這個submitForm是從外部傳進來的,而mapDispatchToPros就是把dispatch操作映射在props上,讓props中有我們需要的函數。所以如果我們不能從其它地方看到明顯的線索的時候,應該考慮去看看這個。

BaseNew.mapDispatchToProps是來自于BaseNew,我們又找到了相應的代碼:

src/features/shared/components/BaseNew.jsx#L9-L16

import actions from "actions"

// ...

export const mapDispatchToProps = (type) => (dispatch) => ({
  submitForm: (data) => {
    return dispatch(actions[type].submitForm(data)).then((resp) => {
      dispatch(actions.tutorial.submitTutorialForm(data, type))
      return resp
    })
  }
})

果然在里面找到了submitForm的定義。在里面第一個dispatch處,傳入了參數actions[type].submitForm(data),這里的type應該是transaction,而actions應該是之前某處定義的各種action的集合。

根據import actions from "actions",我們發現from后面的"actions"不是相對路徑,那么它對應的就是js的源代碼根目錄src下的某個文件,比如actions.js。

找到后打開一看,里面果然有transaction

src/actions.js#L15-L29

// ...
import { actions as transaction } from "features/transactions"
// ...

const actions = {
  // ...
  transaction,
  // ...
}

我們繼續進入features/transactions/探索,很快找到:

src/features/transactions/actions.js#L100-L200

form.submitForm = (formParams) => function (dispatch) {
  // ...
  // 2.
  const buildPromise = connection.request("/build-transaction", {actions: processed.actions})

  const signAndSubmitTransaction = (transaction, password) => {
    // 4. 
    return connection.request("/sign-transaction", {
      password,
      transaction
    }).then(resp => {
      if (resp.status === "fail") {
        throw new Error(resp.msg)
      }

      const rawTransaction = resp.data.transaction.rawTransaction
      // 5. 
      return connection.request("/submit-transaction", {rawTransaction})
    }).then(dealSignSubmitResp)
  }
  // ...
  if (formParams.submitAction == "submit") {
    // 1. 
    return buildPromise
      .then((resp) => {
        if (resp.status === "fail") {
          throw new Error(resp.msg)
        }
        // 3.
        return signAndSubmitTransaction(resp.data, formParams.password)
      })
  }
  // ...
}

上面的代碼經過了我的簡化,其實它本來是有很多分支的(因為表單中除了“簡單交易”還有“高級交易”等情況)。即使如此,也可以看出來這個過程還是比較復雜的,經過了好幾次的后臺接口訪問:

第1處代碼就是對應我們“簡單交易”的情況,它會調用buildPromise,這里面應該包括了對后臺的訪問

第2處就是buildPromise的定義,可以看到會訪問/build-transaction

第3處是如果前一個訪問是正常的,那么會繼續調用signAndSubmitTransaction

第4處就進入到signAndSubmitTransaction內部了,可以看到,它會訪問一個新的接口/sign-transaction

第5處是在前一個正常的情況下,進行最后的提交,訪問接口/submit-transaction。后面的dealSignSubmitResp是一些對前端的操作,所以就不看它了

可以看到,這一個表單的提交,在內部對應著好幾個接口的訪問,每個提交的數據也不一樣,代碼跟蹤起來不太方便。但是好在只要我們知道了這一條主線,那么尋找其它的信息就會簡單一些。不過我們也沒有必要執著于全部從源代碼中找到答案,因為我們的目的并不是學習React/Redux,而是理解比原的邏輯,所以我們可以借助別的工具(比如Chrome的Developer Tools),來捕獲請求的數據,從而推理出邏輯。

我已經從Chrome的開發工具中取得了前端向下面幾個接口發送的數據:

/build-transaction

/sign-transaction

/submit-transaction

但是由于我們在這個小問題中,關注的重點是前端如何把數據提交給后臺的,所以對于這里提交的數據的意義暫時不討論,留待下個小問題中一一解答。

圖1中,后臺是如何接收到轉帳數據并執行轉帳操作的?

由于在圖1中前端一共訪問了3個不同的后端接口,所以在這里我們就需要依次分開討論。

/build-transaction

下面是我通過Chrome的開發工具捕獲的數據,看起來還比較多:

/build-transaction

提交的數據:
{
    "actions": [{
        "amount": 437400,
        "type": "spend_account",
        "receiver": null,
        "account_alias": "freewind",
        "account_id": "",
        "asset_alias": "BTM",
        "reference_data": null
    }, {
        "amount": 23400000000,
        "type": "spend_account",
        "receiver": null,
        "account_alias": "freewind",
        "account_id": "",
        "asset_alias": "BTM",
        "asset_id": "",
        "reference_data": null
    }, {
        "address": "sm1qe4z3ava34wv5njdgekcgdlrckc95gnljazezva",
        "amount": 23400000000,
        "type": "control_address",
        "receiver": null,
        "asset_alias": "BTM",
        "asset_id": "",
        "reference_data": null
    }]
}

可以看到前端向/build-transaction發送的數據包含了三個元素,其中前兩個是來源帳戶的信息,第三個是目的帳戶地址。這三個元素都包含一個叫amount的key,它的值對應的是相應資產的數量,如果是BTM的話,這個數字就需要從右向左數8位,再加上一個小數點。也就是說,第一個amount對應的是0.00437400個BTM,第二個是234.00000000,第三個是234.00000000。

第一個元素對應的費用是gas,也就是圖1中顯示出來的估算的手續費。第二個是要從相應帳戶中轉出234個BTM,第三個是要轉入234個BTM。

另外,前兩個的typespend_account,表明了是帳戶,但是spend是什么意思目前還不清楚(TODO);第三個是control_address,表示是一個地址。

通過這些數據,比原的后臺就知道該怎么做了。

得到的回應:
{
    "status": "success",
    "data": {
        "raw_transaction": "070100010161015f643bef0936443042ccb1e94213ed52af72488088702d88e7fc3580359a19a522ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014108c5ba0934951a12755523f8a1fe42a6c24342f010002013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8ebaabf4201160014b111c8114dc7ee02050598022b46855fd482d27300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d4fe955701160014cd451eb3b1ab9949c9a8cdb086fc78b60b444ff200",
        "signing_instructions": [{
            "position": 0,
            "witness_components": [{
                "type": "raw_tx_signature",
                "quorum": 1,
                "keys": [{
                    "xpub": "f98b3a39b4eef67707cac85240ef07235c990301b2e0658001545bdb7fde3a21363a23682a1dfbb727dec7565624812c314ca9f31a7f7374101e0247d05cb248",
                    "derivation_path": ["010100000000000000", "0100000000000000"]
                }],
                "signatures": null
            }, {
                "type": "data",
                "value": "b826dcccff76d19d097ca207e053e67d67e3da3a90896ae9fa2d984c6f36d16c"
            }]
        }],
        "allow_additional_actions": false
    }
}

這個回應信息是什么意思呢?我們現在開始研究。

我們在比原的后端代碼庫中,通過查找/build-transaction,很快找到了它的定義處:

api/api.go#L164-L244

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

可以看到它對就的方法是a.build,其代碼為:

api/transact.go#L167-L176

func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response {
    subctx := reqid.NewSubContext(ctx, reqid.New())

    tmpl, err := a.buildSingle(subctx, buildReqs)
    if err != nil {
        return NewErrorResponse(err)
    }

    return NewSuccessResponse(tmpl)
}

其中的buildReqs就對應著前端提交過來的參數,只不過被jsonHandler自動轉成了Go代碼。其中BuildRequest是這樣定義的:

api/request.go#L21-L26

type BuildRequest struct {
    Tx        *types.TxData            `json:"base_transaction"`
    Actions   []map[string]interface{} `json:"actions"`
    TTL       json.Duration            `json:"ttl"`
    TimeRange uint64                   `json:"time_range"`
}

可以看出來有一些字段比如base_transaction, ttl, time_range等在本例中并沒有提交上來,它們應該是可選的。

繼續看a.buildSingle

api/transact.go#L101-L164

func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Template, error) {
    // 1.
    err := a.filterAliases(ctx, req)
    // ...

    // 2.
    if onlyHaveSpendActions(req) {
        return nil, errors.New("transaction only contain spend actions, didn"t have output actions")
    }

    // 3.
    reqActions, err := mergeActions(req)
    // ...

    // 4. 
    actions := make([]txbuilder.Action, 0, len(reqActions))
    for i, act := range reqActions {
        typ, ok := act["type"].(string)
        // ...
        decoder, ok := a.actionDecoder(typ)
        // ...
        b, err := json.Marshal(act)
        // ...
        action, err := decoder(b)
        // ...
        actions = append(actions, action)
    }

    // 5. 
    ttl := req.TTL.Duration
    if ttl == 0 {
        ttl = defaultTxTTL
    }
    maxTime := time.Now().Add(ttl)

    // 6. 
    tpl, err := txbuilder.Build(ctx, req.Tx, actions, maxTime, req.TimeRange)
    // ...
    return tpl, nil
}

這段代碼內容還是比較多的,但總體基本上還是對參數進行驗證、補全和轉換,然后交給后面的方法處理。我分成了多塊,依次講解大意:

第1處的filterAliases主要是對傳進來的參數進行驗證和補全。比如像account和asset,一般都有id和alias這兩個屬性,如果只提交了alias而沒有提交id的話,則filterAliases就會從數據庫或者緩存中查找到相應的id補全。如果過程中出了錯,比如alias不存在,則報錯返回

第2處的onlyHaveSpendActions是檢查如果這個交易中,只存在資金來源方,而沒有資金目標方,顯示是不對的,報錯返回

第3處的mergeActions是把請求數據中的spend_account進行分組累加,把相同account的相同asset的數量累加到一起

第4處的代碼看著挺多,實際上只是把剛才處理過的請求數據由JSON轉換成相應的Go對象。在actionDecoder(typ)里通過手動比較type的值返回相應的Decoder

第5處的ttl是指Time To Live,指的這個請求的存活時間,如果沒指明的話(本例就沒有),則設為默認值5分鐘

第6處就是轉交給txbuilder.Build繼續處理

在這幾處里提到的方法和函數的代碼我就不貼出來了,因為基本上都是一些針對map的低級操作,大片大片的看著很累,實際上沒做多少事。這種類型的代碼反復出現,在別的語言中(甚至Java)都可以抽出來很多工具方法,但是在Go里由于語言特性(缺少泛型,麻煩的錯誤處理),似乎不是很容易??匆谎蹚V大Go程序員的期盼:

https://github.com/golang/go/...

看看在Go2中會不會實現。

讓我們繼續看txbuilder.Build

blockchain/txbuilder/txbuilder.go#L40-L79

func Build(ctx context.Context, tx *types.TxData, actions []Action, maxTime time.Time, timeRange uint64) (*Template, error) {
    builder := TemplateBuilder{
        base:      tx,
        maxTime:   maxTime,
        timeRange: timeRange,
    }

    // Build all of the actions, updating the builder.
    var errs []error
    for i, action := range actions {
        err := action.Build(ctx, &builder)
        // ...
    }

    // If there were any errors, rollback and return a composite error.
    if len(errs) > 0 {
        builder.rollback()
        return nil, errors.WithData(ErrAction, "actions", errs)
    }

    // Build the transaction template.
    tpl, tx, err := builder.Build()
    // ...

    return tpl, nil
}

這塊代碼經過簡化后,還是比較清楚的,基本上就是想盡辦法把TemplateBuilder填滿。TemplateBuilder是這樣的:

blockchain/txbuilder/builder.go#L17-L28

type TemplateBuilder struct {
    base                *types.TxData
    inputs              []*types.TxInput
    outputs             []*types.TxOutput
    signingInstructions []*SigningInstruction
    minTime             time.Time
    maxTime             time.Time
    timeRange           uint64
    referenceData       []byte
    rollbacks           []func()
    callbacks           []func() error
}

可以看到有很多字段,但是只要清楚了它們的用途,我們也就清楚了交易transaction是怎么回事。但是我發現一旦深入下去,很快又觸及到比原的核心部分,所以就停在這里不去深究了。前面Build函數里面提到的其它的方法,比如action.Build等,我們也不進去了,因為它們基本上都是在想盡辦法組裝出最后需要的對象。

到這里,我們可以認為buildSingle就走完了,然后回到func (a *API) build(...),把生成的對象返回給前端。

那么,這個接口/build-transaction到底是做什么的呢?通過上面我分析,我們可以知道它有兩個作用:

一是檢查各參數是否正確。因為用戶填寫的數據很多,而且里面的數據看起來專業性很強,容易出錯,早點發現早點提醒

二是補全一些信息,如id,公鑰等等,方便前端進行后面的操作

在這個接口的分析過程中,我們還是忽略了很多內容,比如返回給客戶端的那一大段JSON代碼中的數據。我想這些東西還是留著我們研究到比原的核心的時候,再一起學習吧。

/sign-transaction

在前一步/build-transaction成功完成以后,會進行下一步操作/sign-transaction。

下面是通過Chrome的開發工具捕獲的內容:

提交的數據:
{
    "password": "my-password",
    "transaction": {
        "raw_transaction": "070100010161015f643bef0936443042ccb1e94213ed52af72488088702d88e7fc3580359a19a522ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014108c5ba0934951a12755523f8a1fe42a6c24342f010002013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8ebaabf4201160014b111c8114dc7ee02050598022b46855fd482d27300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d4fe955701160014cd451eb3b1ab9949c9a8cdb086fc78b60b444ff200",
        "signing_instructions": [{
            "position": 0,
            "witness_components": [{
                "type": "raw_tx_signature",
                "quorum": 1,
                "keys": [{
                    "xpub": "f98b3a39b4eef67707cac85240ef07235c990301b2e0658001545bdb7fde3a21363a23682a1dfbb727dec7565624812c314ca9f31a7f7374101e0247d05cb248",
                    "derivation_path": ["010100000000000000", "0100000000000000"]
                }],
                "signatures": null
            }, {
                "type": "data",
                "value": "b826dcccff76d19d097ca207e053e67d67e3da3a90896ae9fa2d984c6f36d16c"
            }]
        }],
        "allow_additional_actions": false
    }
}

可以看到這里提交的請求數據,與前面/build-transaction相比,基本上是一樣的,只是多了一個password,即我們剛才在表單最后一處填寫的密碼。從這個接口的名字中含有sign可以推測,這一步應該是與簽名有關。

得到的回應
{
    "status": "success",
    "data": {
        "transaction": {
            "raw_transaction": "070100010161015f643bef0936443042ccb1e94213ed52af72488088702d88e7fc3580359a19a522ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014108c5ba0934951a12755523f8a1fe42a6c24342f630240c52a057fa26322a48fdd88c842cf31a84c6aec54ae2dc62554dc3c7e0216986a0a4f4a5c935a5ae6d88b4c7a4d1ca1937205f5eb23089128cc6744fbd2b88d0520b826dcccff76d19d097ca207e053e67d67e3da3a90896ae9fa2d984c6f36d16c02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8ebaabf4201160014b111c8114dc7ee02050598022b46855fd482d27300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d4fe955701160014cd451eb3b1ab9949c9a8cdb086fc78b60b444ff200",
            "signing_instructions": [{
                "position": 0,
                "witness_components": [{
                    "type": "raw_tx_signature",
                    "quorum": 1,
                    "keys": [{
                        "xpub": "f98b3a39b4eef67707cac85240ef07235c990301b2e0658001545bdb7fde3a21363a23682a1dfbb727dec7565624812c314ca9f31a7f7374101e0247d05cb248",
                        "derivation_path": ["010100000000000000", "0100000000000000"]
                    }],
                    "signatures": ["c52a057fa26322a48fdd88c842cf31a84c6aec54ae2dc62554dc3c7e0216986a0a4f4a5c935a5ae6d88b4c7a4d1ca1937205f5eb23089128cc6744fbd2b88d05"]
                }, {
                    "type": "data",
                    "value": "b826dcccff76d19d097ca207e053e67d67e3da3a90896ae9fa2d984c6f36d16c"
                }]
            }],
            "allow_additional_actions": false
        },
        "sign_complete": true
    }
}

回過來的消息也基本上跟提交的差不多,只是在成功操作后,raw_transaction字段的內容也變長了,還添加上了signatures字段。

我們開始看代碼,通過搜索/sign-transaction,我們很快定位到以下代碼:

api/api.go#L164-L244

func (a *API) buildHandler() {
    // ...
    if a.wallet != nil {
        // ...
        m.Handle("/sign-transaction", jsonHandler(a.pseudohsmSignTemplates))
        // ...
}

/sign-transaction對應的handler是a.pseudohsmSignTemplates,讓我們跟進去:

api/hsm.go#L53-L63

func (a *API) pseudohsmSignTemplates(ctx context.Context, x struct {
    Password string             `json:"password"`
    Txs      txbuilder.Template `json:"transaction"`
}) Response {
    if err := txbuilder.Sign(ctx, &x.Txs, x.Password, a.pseudohsmSignTemplate); err != nil {
        log.WithField("build err", err).Error("fail on sign transaction.")
        return NewErrorResponse(err)
    }
    log.Info("Sign Transaction complete.")
    return NewSuccessResponse(&signResp{Tx: &x.Txs, SignComplete: txbuilder.SignProgress(&x.Txs)})
}

可以看到這個方法內容也是比較簡單的。通過調用txbuilder.Sign,把前端傳來的參數傳進去,然后把結果返回給前端即可。那我們只需要看txbuilder.Sign即可:

blockchain/txbuilder/txbuilder.go#L82-L100

func Sign(ctx context.Context, tpl *Template, auth string, signFn SignFunc) error {
    // 1. 
    for i, sigInst := range tpl.SigningInstructions {
        for j, wc := range sigInst.WitnessComponents {
            switch sw := wc.(type) {
            case *SignatureWitness:
                err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
                // ...
            case *RawTxSigWitness:
                err := sw.sign(ctx, tpl, uint32(i), auth, signFn)
            // ...
            }
        }
    }
    // 2.
    return materializeWitnesses(tpl)
}

可以看到這段代碼邏輯還是比較簡單:

第1處代碼是兩個大循環,基本上做了兩件事:

把用戶提交上來的數據中需要簽名的部分取出來,運行相關的簽名函數sw.sign,生成相關的簽名signatures

raw_transaction處添加了一些操作符和約束條件,把它變成了一個合約(這塊還需要以后確認)

第2處代碼如果發現前面簽名過程正確,就調用materializeWitnesses函數。它主要是在檢查沒有數據錯誤之后,把第1步中生成的簽名signatures添加到tpl對象上去。

由于sw.SignmaterializeWitnesses基本上都是一些算法或者合約相關的東西,我們這里就暫時忽略,以后再研究吧。

這個接口/sign-transaction的作用應該是對通過密碼以及公鑰對“交易”這個重要的操作進行驗證,不然大家都能隨便把別人的錢轉到自己帳戶里了。

/submit-transaction

當前一步/sign-transaction簽名成功之后,終于可以進行最后一步/submit-transaction進行最終的提交了。

下面是通過Chrome的開發工具捕獲的內容。

請求的數據
{
    "raw_transaction": "070100010161015f643bef0936443042ccb1e94213ed52af72488088702d88e7fc3580359a19a522ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d599010001160014108c5ba0934951a12755523f8a1fe42a6c24342f630240c52a057fa26322a48fdd88c842cf31a84c6aec54ae2dc62554dc3c7e0216986a0a4f4a5c935a5ae6d88b4c7a4d1ca1937205f5eb23089128cc6744fbd2b88d0520b826dcccff76d19d097ca207e053e67d67e3da3a90896ae9fa2d984c6f36d16c02013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe8ebaabf4201160014b111c8114dc7ee02050598022b46855fd482d27300013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80d4fe955701160014cd451eb3b1ab9949c9a8cdb086fc78b60b444ff200"
}

可以看到,到了這一步,提交的數據就少了,直接把前一步生成的簽名后的raw_transaction提交上去就行了。我想這里的內容應該已經包含了全部需要的信息,并且經過了驗證,所以不需要其它數據了。

得到的回應
{
    "status": "success",
    "data": {
        "tx_id": "6866c1ab2bfa2468ce44451ce6af2a83f3885cdb6a1673fec94b27f338acf9c5"
    }
}

可以看到成功提交后,會得到一個tx_id,即為當前這個交易生成的唯一的id,可以用來查詢。

我們通過查找/submit-transaction,可以在代碼中找到:

api/api.go#L164-L244

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

那么/submit-transaction所對應的handler就是a.submit了。我們跟進去:

api/transact.go#L182-L191

func (a *API) submit(ctx context.Context, ins struct {
    Tx types.Tx `json:"raw_transaction"`
}) Response {
    if err := txbuilder.FinalizeTx(ctx, a.chain, &ins.Tx); err != nil {
        return NewErrorResponse(err)
    }

    log.WithField("tx_id", ins.Tx.ID).Info("submit single tx")
    return NewSuccessResponse(&submitTxResp{TxID: &ins.Tx.ID})
}

可以看到主要邏輯就是調用txbuilder.FinalizeTx來“終結”這個交易,然后把生成的tx_id返回給前端。

讓我們繼續看txbuilder.FinalizeTx

blockchain/txbuilder/finalize.go#L25-L47

func FinalizeTx(ctx context.Context, c *protocol.Chain, tx *types.Tx) error {
    // 1.
    if err := checkTxSighashCommitment(tx); err != nil {
        return err
    }

    // This part is use for prevent tx size  is 0
    // 2.
    data, err := tx.TxData.MarshalText()
    // ...
    
    // 3.
    tx.TxData.SerializedSize = uint64(len(data))
    tx.Tx.SerializedSize = uint64(len(data))

    // 4.
    _, err = c.ValidateTx(tx)
    // ...
}

這一個方法整體上還是各種驗證

第1處代碼是對交易對象簽名相關的內容進行嚴格的檢查,比如參數個數、簽名、甚至某些對應虛擬機的操作碼,這一塊挺復雜的,你一定不會想看blockchain/txbuilder/finalize.go#L66-L113

第2處代碼是把交易數據解碼,從看起來奇怪的16進制字符串變成正常的內容

第3處代碼是把解析出來的內容的長度賦值給tx中的某些字段

第4處代碼是對交易內容再次進行詳細的檢查,最后還包括了對gas的檢查,如果全部正常,則會把它提交到txPool(用來在內存中保存交易的對象池),等待廣播出去以及打包到區塊中。我覺得這個名字ValidateTx有點問題,因為它即包含了驗證,還包含了提交到池子中,這是兩個不同的操作,應該分開

這里涉及到的更細節的代碼就不進去了,主線我們已經有了,感興趣的同學可以自行進去深入研究。

那我們今天關于提交交易的這個小問題就算是完成了,下次會繼續研究剩下的幾個小問題。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/24177.html

相關文章

  • 剝開原看代碼12:比原如何通過/create-account-receiver創建地址的?

    摘要:繼續看生成地址的方法由于這個方法里傳過來的是而不是對象,所以還需要再用查一遍,然后,再調用這個私有方法創建地址該方法可以分成部分在第塊中主要關注的是返回值。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在比原的dashboard中...

    oneasp 評論0 收藏0
  • 剝開原看代碼03:比原如何監聽p2p端口的

    摘要:啟動直到進入所以我們首先需要知道,比原在源代碼中是如何啟動,并且一步步走進了的世界。后面省略了一些代碼,主要是用來獲取當前監聽的實際以及外網,并記錄在日志中。 比原是如何監聽p2p端口的 我們知道,在使用bytomd init --chain_id mainnet/testnet/solonet初始化比原的時候,它會根據給定的chain_id的不同,使用不同的端口(參看config/t...

    layman 評論0 收藏0
  • 剝開原看代碼11:比原如何通過接口/create-account創建帳戶的

    摘要:而本文將繼續討論,比原是如何通過接口來創建帳戶的。把各信息打包在一起,稱之為另外,在第處還是一個需要注意的。比原在代碼中使用它保存各種數據,比如區塊帳戶等。到這里,我們已經差不多清楚了比原的是如何根據用戶提交的參數來創建帳戶的。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://git...

    haobowd 評論0 收藏0
  • 剝開原看代碼06:比原如何把請求區塊數據的信息發出去的

    摘要:作者比原項目倉庫地址地址在前一篇中,我們說到,當比原向其它節點請求區塊數據時,會發送一個把需要的區塊告訴對方,并把該信息對應的二進制數據放入對應的通道中,等待發送。這個就是真正與連接對象綁定的一個緩存區,寫入到它里面的數據,會被發送出去。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https:...

    CloudwiseAPM 評論0 收藏0
  • 剝開原看代碼10:比原如何通過/create-key接口創建密鑰的

    摘要:如果傳的是,就會在內部使用默認的隨機數生成器生成隨機數并生成密鑰。使用的是,生成的是一個形如這樣的全球唯一的隨機數把密鑰以文件形式保存在硬盤上。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前一篇,我們探討了從瀏覽器的dashb...

    ccj659 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<