Bytom的鏈式交易和花費未確認的交易

當我們基於比原做應用的時候,在構建交易過程中會遇到以下兩種情況。多個地址向一個地址轉賬,還有一種就是從一個地址分批次向多個地址轉賬。那我們今天就來介紹一下這兩種交易構建的具體流程,以及貼出具體實現的代碼。

鏈式交易

當我們從多個錢包地址一次性轉到一個地址的時候,爲了提高用戶體驗。我們可以選擇鏈式交易,把多筆交易一次性打包。那我們下面就來看一下鏈式交易的流程。

接下來我們來看一下build-transaction接口的代碼實現過程,代碼如下:

// POST /build-chain-transactions
func (a *API) buildChainTxs(ctx context.Context, buildReqs *BuildRequest) Response {
    //驗證請求id
    subctx := reqid.NewSubContext(ctx, reqid.New())
    //構建交易,方法的具體過程在下面
    tmpls, err := a.buildTxs(subctx, buildReqs)
    if err != nil {
       return NewErrorResponse(err)
     }
     return NewSuccessResponse(tmpls)
}

核心的實現方法,buildTxs方法的實現如下:

func (a *API) buildTxs(ctx context.Context, req *BuildRequest) ([]*txbuilder.Template, error) {
//驗證參數的合法性
if err := a.checkRequestValidity(ctx, req); err != nil {
    return nil, err
}
//合併處理交易輸入輸出的類型組合
actions, err := a.mergeSpendActions(req)
if err != nil {
    return nil, err
}
//構建一筆新的交易模板
builder := txbuilder.NewBuilder(time.Now().Add(req.TTL.Duration))
//聲明交易模板
tpls := []*txbuilder.Template{}
//遍歷交易的輸入輸出類型組合
for _, action := range actions {
    //類型組合的輸入爲apend_account
    if action.ActionType() == "spend_account" {
       //構建花費的輸入輸出類型組合並且自動合併UTXO
        tpls, err = account.SpendAccountChain(ctx, builder, action)
    } else {
       //構建交易輸入輸出類型組合
        err = action.Build(ctx, builder)
    }

    if err != nil {
       //回滾
        builder.Rollback()
        return nil, err
    }
}
//構建交易
tpl, _, err := builder.Build()
if err != nil {
   //回滾
    builder.Rollback()
    return nil, err
}

tpls = append(tpls, tpl)
return tpls, nil
}

build方法的實現過程:

// Build build transactions with template
func (b *TemplateBuilder) Build() (*Template, *types.TxData, error) {
// Run any building callbacks.
for _, cb := range b.callbacks {
    err := cb()
    if err != nil {
        return nil, nil, err
    }
}

tpl := &Template{}
tx := b.base
if tx == nil {
    tx = &types.TxData{
        Version: 1,
    }
}

if b.timeRange != 0 {
    tx.TimeRange = b.timeRange
}

// Add all the built outputs.
tx.Outputs = append(tx.Outputs, b.outputs...)

// Add all the built inputs and their corresponding signing instructions.
for i, in := range b.inputs {
    instruction := b.signingInstructions[i]
    instruction.Position = uint32(len(tx.Inputs))

    // Empty signature arrays should be serialized as empty arrays, not null.
    if instruction.WitnessComponents == nil {
        instruction.WitnessComponents = []witnessComponent{}
    }
    tpl.SigningInstructions = append(tpl.SigningInstructions, instruction)
    tx.Inputs = append(tx.Inputs, in)
}

tpl.Transaction = types.NewTx(*tx)
tpl.Fee = CalculateTxFee(tpl.Transaction)
return tpl, tx, nil
}

到此,我們的鏈式交易的代碼到此就講解到這兒。如果感興趣想仔細閱讀源碼,點擊源碼地址:https://git.io/fhAsr

花費未確認的交易

下面我們來介紹一下花費未確認的交易,我們首先介紹一下什麼是花費未確認的交易。我們知道UTXO模型在交易的過程中,如果交易未打包確認。再進行第二筆轉賬就會存在“雙花”問題,就不能再發起交易或者需要等一段時間才能再發起一筆交易。如果使用花費未確認的交易就可以避免這個問題。

那麼花費未確認的交易實現機制是什麼樣的呢?我們在創建第一筆交易的時候,會找零,此時交易是未確認的狀態。找零存在交易池中,我們發第二筆交易的時候就直接使用在交易池中找零地址裏面的資產。

那我們來看一下花費未確認交易的代碼實現過程,花費過程結構體如下:

type spendAction struct {
   accounts *Manager  //存儲賬戶及其相關的控制程序參數
   bc.AssetAmount     //資產id和資產數量的結構體
   AccountID      string `json:"account_id"`  //賬戶id
   UseUnconfirmed bool   `json:"use_unconfirmed"`  //是否未確認
}

方法如下:

// MergeSpendAction merge common assetID and accountID spend action
func MergeSpendAction(actions []txbuilder.Action) []txbuilder.Action {
   //聲明變量,map
   resultActions := []txbuilder.Action{}
   spendActionMap := make(map[string]*spendAction)
   //遍歷交易的輸入輸出類型組合
   for _, act := range actions {
       switch act := act.(type) {
       case *spendAction:
          //actionKey字符串拼接
           actionKey := act.AssetId.String() + act.AccountID
           //遍歷spendActionMap
           if tmpAct, ok := spendActionMap[actionKey]; ok {
               tmpAct.Amount += act.Amount
               tmpAct.UseUnconfirmed = tmpAct.UseUnconfirmed || act.UseUnconfirmed
            } else {
               spendActionMap[actionKey] = act
               resultActions = append(resultActions, act)
        }
      default:
        resultActions = append(resultActions, act)
      }
   }
   return resultActions
}

上面只是簡單的貼出了核心的實現代碼,如果感興趣想仔細閱讀源碼,點擊地址:https://git.io/fhAsw

這一期的內容我們到此就結束了,如果你感興趣可以加入我們的社區一起討論。如果在閱讀的過程中有什麼疑問可以在下方給我們留言,我們將第一時間爲你解答。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章