博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
剥开比原看代码15:比原是如何转帐的
阅读量:6897 次
发布时间:2019-06-27

本文共 18423 字,大约阅读时间需要 61 分钟。

作者:freewind

比原项目仓库:

Github地址:

Gitee地址:

在前面几篇中,我们做了足够了准备,现在终于可以试一试转帐功能了!

这里的转帐最好使用solonet,再按前一篇文章的办法修改代码后产生单机测试币,然后再试。在此之前,如果需要的话,请先备份好你之前的帐户,然后删除(或重命名)你的数据目录,再使用bytomd init --chain_id=solonet重新初始化。

下面是我通过dashboard进行的转帐操作,在操作之前,我先建立了两个帐户,然后把钱从一个帐户转到另一个帐户的地址中:

新建一个交易,填上把哪个帐户的哪种资产转到某个地址上。可以看到还要消耗一定的gas:

img_c815d4f0f71773829ab93f26b0fdac38.png

(上图为图1)

转帐成功后,如下:

img_589000e82808cdc41a92f1f1053ab397.png

(上图为图2)

我们看一下这个交易的详细信息,由于太长,截成了两个图:

img_63c11e60a5e582dd408d2a2bdea3424c.png

img_9eac917a8af478519aa2b8bc6ab81d19.png

(上面两图合称为图3)

我们今天(以及往后的几天)就是把这一块流程搞清楚。

由于上面展示的操作还是有点多的,所以我们还是按之前的套路,先把它分解成多个小问题,一一解决:

  1. 图1中,转帐界面是如何把转帐数据提交到后台的?
  2. 图1中,后台是如何接收到转帐数据并执行转帐操作的?
  3. 图2中,前台是如何拿到后台的数据并展示出来的?
  4. 图3中,前台是如何拿到后台的数据并展示出来的?

今天的文章,我们主要是研究前两个问题,即跟图1相关的逻辑。

图1中,转帐表单是如何把转帐数据提交到后台的?

由于是前端,所以我们要去从前端的代码库中寻找。通过搜索“简单交易”这个词,我们很快定位到下面这块代码:

return (      
// ...
)

由于上面的代码实在太长太细节,全是一些jsx用于生成表单的代码,我们就跳过算了,有兴趣的同学可以自行看细节。我们需要关注的是,当我们单击了“提交交易”的按钮以后,this.submitWithValidation会被调用,而它对应的代码是:

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连接起来的代码,非常有用:

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,我们又找到了相应的代码:

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

// ...import { actions as transaction } from 'features/transactions'// ...const actions = {  // ...  transaction,  // ...}

我们继续进入features/transactions/探索,很快找到:

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. 第1处代码就是对应我们“简单交易”的情况,它会调用buildPromise,这里面应该包括了对后台的访问
  2. 第2处就是buildPromise的定义,可以看到会访问/build-transaction
  3. 第3处是如果前一个访问是正常的,那么会继续调用signAndSubmitTransaction
  4. 第4处就进入到signAndSubmitTransaction内部了,可以看到,它会访问一个新的接口/sign-transaction
  5. 第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,很快找到了它的定义处:

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

可以看到它对就的方法是a.build,其代码为:

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是这样定义的:

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

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. 第1处的filterAliases主要是对传进来的参数进行验证和补全。比如像account和asset,一般都有id和alias这两个属性,如果只提交了alias而没有提交id的话,则filterAliases就会从数据库或者缓存中查找到相应的id补全。如果过程中出了错,比如alias不存在,则报错返回
  2. 第2处的onlyHaveSpendActions是检查如果这个交易中,只存在资金来源方,而没有资金目标方,显示是不对的,报错返回
  3. 第3处的mergeActions是把请求数据中的spend_account进行分组累加,把相同account的相同asset的数量累加到一起
  4. 第4处的代码看着挺多,实际上只是把刚才处理过的请求数据由JSON转换成相应的Go对象。在actionDecoder(typ)里通过手动比较type的值返回相应的Decoder
  5. 第5处的ttl是指Time To Live,指的这个请求的存活时间,如果没指明的话(本例就没有),则设为默认值5分钟
  6. 第6处就是转交给txbuilder.Build继续处理

在这几处里提到的方法和函数的代码我就不贴出来了,因为基本上都是一些针对map的低级操作,大片大片的看着很累,实际上没做多少事。这种类型的代码反复出现,在别的语言中(甚至Java)都可以抽出来很多工具方法,但是在Go里由于语言特性(缺少泛型,麻烦的错误处理),似乎不是很容易。看一眼广大Go程序员的期盼:

img_cd179ef006978eebe920f8438c978775.png

看看在Go2中会不会实现。

让我们继续看txbuilder.Build

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是这样的:

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到底是做什么的呢?通过上面我分析,我们可以知道它有两个作用:

  1. 一是检查各参数是否正确。因为用户填写的数据很多,而且里面的数据看起来专业性很强,容易出错,早点发现早点提醒
  2. 二是补全一些信息,如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,我们很快定位到以下代码:

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

/sign-transaction对应的handler是a.pseudohsmSignTemplates,让我们跟进去:

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即可:

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. 第1处代码是两个大循环,基本上做了两件事:
    1. 把用户提交上来的数据中需要签名的部分取出来,运行相关的签名函数sw.sign,生成相关的签名signatures
    2. raw_transaction处添加了一些操作符和约束条件,把它变成了一个合约(这块还需要以后确认)
  2. 第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,可以在代码中找到:

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

那么/submit-transaction所对应的handler就是a.submit了。我们跟进去:

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

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. 第1处代码是对交易对象签名相关的内容进行严格的检查,比如参数个数、签名、甚至某些对应虚拟机的操作码,这一块挺复杂的,你一定不会想看
  2. 第2处代码是把交易数据解码,从看起来奇怪的16进制字符串变成正常的内容
  3. 第3处代码是把解析出来的内容的长度赋值给tx中的某些字段
  4. 第4处代码是对交易内容再次进行详细的检查,最后还包括了对gas的检查,如果全部正常,则会把它提交到txPool(用来在内存中保存交易的对象池),等待广播出去以及打包到区块中。我觉得这个名字ValidateTx有点问题,因为它即包含了验证,还包含了提交到池子中,这是两个不同的操作,应该分开

这里涉及到的更细节的代码就不进去了,主线我们已经有了,感兴趣的同学可以自行进去深入研究。

那我们今天关于提交交易的这个小问题就算是完成了,下次会继续研究剩下的几个小问题。

转载地址:http://bpcdl.baihongyu.com/

你可能感兴趣的文章
[Javascript] Gradient Fills on the HTML5 Canvas
查看>>
对CAB文件进行数字签名
查看>>
SQL Server 变更数据捕获(CDC)
查看>>
Starting httpd:Could not reliably determine the server's fully qualified domain name
查看>>
2015第19周一
查看>>
datatables 参数详解(转)
查看>>
[福利]非认证公众帐号也能申请微信连Wi-Fi了
查看>>
什么时候用Application的Context,什么时候用Activity的Context
查看>>
解读ASP.NET 5 & MVC6系列(15):MvcOptions配置
查看>>
东大oj-1591 Circle of friends
查看>>
动态IP无法获取默认网关,显示0.0.0.0的解决办法
查看>>
课本[Teb]软件设计
查看>>
[原创]推荐一些在线API生成工具
查看>>
unity5, UI Button "On Button Down"
查看>>
基于注解Spring MVC综合Hibernate(需要jar包,spring和Hibernate整合配置,springMVC组态,重定向,)批量删除...
查看>>
使用命令行备份指定文件夹并保留最新N份
查看>>
关于软件测试人员能力模型的建立(from知乎)
查看>>
匿名管道
查看>>
多线程——继承Thread类别
查看>>
file_operations结构体解析 1
查看>>