Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
1 ?0 }- B1 b! ~8 x0 {* k) vGithub地址:https://github.com/Bytom/bytom( W4 {  A  H! r$ g7 X" l: L0 |; D7 t! i
Gitee地址:https://gitee.com/BytomBlockchain/bytom- E) J, k; r1 E3 I
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。3 E8 q; b# ]5 m8 m7 `& n0 p
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链
0 a) }3 c5 a9 P; b9 O! G$ y
1 G! t  g( k6 R# u注意事项:) K; c$ C' o) s  ~0 c/ G! k
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L2558 E( C5 _$ {6 m
1.创建私钥和公钥
0 O7 h# i7 U# Z- o该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥
' d  g( x+ `9 I" V# Vfunc NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
  J8 U4 q+ N& {7 Z, B) i. m        xprv, err = NewXPrv(r)* }2 K" z( @8 o
        if err != nil {
1 l4 l8 h$ m* S                return% s% p. \" a" x% m" H
        }9 F, [6 _) u. p8 ~
        return xprv, xprv.XPub(), nil" H7 Q" Z- b( ^# U& B* z
}7 q; v9 @3 G: s' w
2.根据公钥创建接收对象
3 P- v+ Y1 ~% o& J- t; ~接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:: I+ V6 }; u% y- u
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {) [- D: R4 ~8 a$ T0 j: q+ C
        pubKey := xpub.PublicKey(): k  a$ X1 Y, F3 o5 ?
        pubHash := crypto.Ripemd160(pubKey)3 U, a$ s+ W4 @2 K: [6 f% J
        // TODO: pass different params due to config
0 e* |, `; r) i# ~# f        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
* c: z/ ?* d& Z5 c        if err != nil {
) Z2 p" h' Y# j  N                return nil, err( v% g& X" m( H
        }
  z/ O( H: X* N( O+ Y& E3 D9 A0 g        control, err := vmutil.P2WPKHProgram([]byte(pubHash))+ G9 y0 e: g: x5 ?: a6 d+ _- ?' v
        if err != nil {
/ t& H( Z4 W" y( A  T                return nil, err
( O  a: \$ I! m6 @        }
; x% K* x+ g+ W% w        return &CtrlProgram{
5 V& \% ^3 t5 n, p. c& f3 H' N                Address:        address.EncodeAddress(),
  f) e. ]% L8 k; v+ n8 z! C                ControlProgram: control,2 y( R1 s8 o  E
        }, nil. z3 D$ N9 w* a4 c+ q
}
9 Y4 _7 H1 j/ N$ W& ^9 B9 z. ?; s创建多签地址参考代码account/accounts.go#L294进行相应改造为:
; s) z  R6 o& |  y6 T9 d% vfunc (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {+ y# \& v% N2 c6 l7 ?; V
        derivedPKs := chainkd.XPubKeys(xpubs); {" v0 Q& W7 @( g
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
- n% v# d6 v7 S/ M/ K2 A        if err != nil {9 D+ e5 ]0 C4 k- q# b& e: C
                return nil, err
  @+ I' w2 \0 j( Q5 E        }- C, ?7 o/ V4 D. d) I' g9 Q- x$ X
        scriptHash := crypto.Sha256(signScript)
7 b# S' @3 F# c4 |3 j, i        // TODO: pass different params due to config: f( d' e( p8 \  T0 R( A( u  z
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams), ~% x0 ^8 D) r( k, n- Y
        if err != nil {
, b1 O3 X2 R% p- X; H3 _7 o                return nil, err
! _$ X; G& h- @* x9 D$ j        }/ c7 h  z3 X( @" e/ o; P. g
        control, err := vmutil.P2WSHProgram(scriptHash), b: E( d" x8 p3 C! p7 ]
        if err != nil {4 S4 v7 w1 U6 V7 U- r
                return nil, err
9 S' x3 ?+ w0 V8 J/ c  [: Z        }. A+ b- c6 e7 Y4 P: B5 |
        return &CtrlProgram{
  U" a9 F/ }( h' K) Y7 w( I1 N                Address:        address.EncodeAddress(),
2 {: e. x/ S8 U6 f  E* b- F                ControlProgram: control,7 Z" F. v! l2 K& E5 n7 c
        }, nil! q! A5 W' s! J& H
}
! k" [, D( G: n. t1 m5 R( C3.找到可花费的utxo  J$ L8 F) h9 u  |
找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)
# d8 I" G" l- x// UTXO describes an individual account utxo.7 t% e) r6 M) ?0 |+ V' N' I
type UTXO struct {
3 Z" A' _. j1 v5 [! H8 ~        OutputID bc.Hash8 G* O5 g9 f- O) N. j
        SourceID bc.Hash
2 F" E/ W$ p5 ?% }. Z, E1 r        // Avoiding AssetAmount here so that new(utxo) doesn't produce an# j) \8 j; f/ ]( t! k# B8 E! z" z
        // AssetAmount with a nil AssetId.+ y- q) p8 {7 [% \, ^7 [
        AssetID bc.AssetID' P+ y0 ~4 |% \+ e7 l
        Amount  uint64
- p7 M6 E! Y  p7 f        SourcePos      uint642 R! m/ W6 V* b
        ControlProgram []byte) d  R; b" k7 B4 G4 h" H
        AccountID           string
0 z0 j) k, A3 T. o2 L& t        Address             string
6 g. j7 @* _) _, \" \7 q        ControlProgramIndex uint64
2 w% V% A5 U" {' a2 H% C! b* a        ValidHeight         uint64
! a: g, U( {0 }' d2 }6 c; M* R% e        Change              bool4 F- c+ L  J& F; Z9 h
}
) F) h+ H. q9 R5 o" H2 F涉及utxo构造交易的相关字段说明如下:4 i( f7 @0 S: [' Q
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址0 y% T, b! J: |& f: c

, r" B" i9 Z2 T) W7 d上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)
8 o% n' v0 U# f  W( [& T$ `. `9 Q// BlockTx is the tx struct for getBlock func
: f& B4 `* T6 S! I7 f* i* P. \type BlockTx struct {
  L/ r) f7 ^/ [0 d        ID         bc.Hash                  `json:"id"`
  o2 y! V* \0 S5 B. r0 N        Version    uint64                   `json:"version"`& {3 {! T5 U7 T
        Size       uint64                   `json:"size"`- H2 \( F- D9 P  g2 }
        TimeRange  uint64                   `json:"time_range"`' [& o0 e8 u. q  M2 S- R
        Inputs     []*query.AnnotatedInput  `json:"inputs"`' j9 ?7 R. G, I
        Outputs    []*query.AnnotatedOutput `json:"outputs"`
# R, b( z  P* v" W" ^$ B        StatusFail bool                     `json:"status_fail"`: h- B5 C0 w6 a
        MuxID      bc.Hash                  `json:"mux_id"`8 g5 I1 ?3 s. P/ A# M
}
/ j, Q# ^8 V6 p4 a- N, A9 v//AnnotatedOutput means an annotated transaction output./ H7 z# U# Y8 N, E
type AnnotatedOutput struct {& g* B3 w$ Y. ]3 o
        Type            string             `json:"type"`  J& e& T9 i+ ]$ @
        OutputID        bc.Hash            `json:"id"`' l0 R* Y% {9 L  W
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`! ]6 {* \7 e3 r- A+ e
        Position        int                `json:"position"`) N" `/ o) B6 h  ?: B6 v, E7 Y% q
        AssetID         bc.AssetID         `json:"asset_id"`2 F9 |4 U. b6 v% ^- p4 k1 r3 c& c
        AssetAlias      string             `json:"asset_alias,omitempty"`( ~* b/ I7 C1 @3 G. R3 p) Y' ?& n% o
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`5 ~2 }8 Z' P1 c) L( U3 `* ^8 _
        Amount          uint64             `json:"amount"`1 n8 i8 ^9 ?" U4 H9 g- i" L; p/ Y
        AccountID       string             `json:"account_id,omitempty"`% w9 Z, i, Q4 Y
        AccountAlias    string             `json:"account_alias,omitempty"`
6 J5 |2 l! X: _. ^0 S        ControlProgram  chainjson.HexBytes `json:"control_program"`9 O( h. J, `9 p9 L
        Address         string             `json:"address,omitempty"`$ v  U. L# [- b# Q# P7 z
}
% `& l1 i8 L' X+ \% G, A* eutxo跟get-block返回结果的字段对应关系如下:
" i$ x8 Q# d* n' h; v`SourceID`       - `json:"mux_id"`
7 ^4 u! U) D. \; t' N- G' F4 n`AssetID`        - `json:"asset_id"`
7 P0 s7 b3 d0 Y# @% ~3 q`Amount`         - `json:"amount"`
( Z, T5 ]! o& h" i$ M`SourcePos`      - `json:"position"`
# L: ?/ {$ N0 v7 \6 G`ControlProgram` - `json:"control_program"`
8 \; z5 H0 |( ~( g& _4 X' \  O`Address`        - `json:"address,omitempty"`
1 a$ [. }; f, K4.通过utxo构造交易
# K$ o8 p) y" z; {" |  y通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。
% m# y2 ^# g  v: C8 m; Y* h% v第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:
( J( d1 U- }$ M4 I// UtxoToInputs convert an utxo to the txinput
0 _$ S2 j  d- P4 U3 C1 J1 g' Bfunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {& m! {9 ^0 d% R& [1 k2 S
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
0 ~4 V3 {) a' C4 d4 a; n        sigInst := &txbuilder.SigningInstruction{}. F3 [& c5 G" B
        if u.Address == "" {
1 l$ {5 J) w8 D' i                return txInput, sigInst, nil- ~$ R' p4 H8 ?* C8 Q. Y% D# q: \9 R
        }( L4 ?- p* ^/ Z0 V' G
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
# _# J8 D1 K3 X, n8 J7 V: b        if err != nil {
3 q9 q2 G5 x4 v* m5 V. u                return nil, nil, err! _7 @. N, R1 Y: ]* y' j
        }% G2 Y6 D) Z' v  ?9 B+ B" `1 o! W
        switch address.(type) {
+ r( N4 X1 E9 z/ D9 t        case *common.AddressWitnessPubKeyHash:5 p/ w4 O- V4 ~: {" Q& m& h
                derivedPK := xpubs[0].PublicKey()
9 c& Y1 ]5 j" h, A# R+ Z# b: g' ^                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
  W" ~7 A4 {/ Y- D        case *common.AddressWitnessScriptHash:' Q( x2 B' q9 t+ d+ D) ^
                derivedPKs := chainkd.XPubKeys(xpubs)
' V. ]3 [' c4 u8 e                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))4 x9 s$ S" \& ?9 E/ P; E& `: v0 ?
                if err != nil {8 J) J% Y# P1 ]8 G9 ]
                        return nil, nil, err
7 F+ h- L7 W5 @, w                }- G( A- ?$ x8 Y. d
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
6 a2 U9 D! i' ]  r9 I0 Q7 D        default:+ p4 o7 W) z3 h0 d( j8 Q! [5 `5 V6 d
                return nil, nil, errors.New("unsupport address type")
) a. J& b2 V8 K) z- F        }
- r* h1 ?& A$ K0 W9 @/ g        return txInput, sigInst, nil, \1 H( n9 ?% `5 b
}) \) }* j# f- |0 m6 D5 c4 E
第二步,通过utxo构造交易输出TxOutput
* b4 Q/ K  F: ]8 o; s: e( e9 u该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
" A+ L9 t3 Q$ K. H# W# s// NewTxOutput create a new output struct, r) y, T, Q; f' u
func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
  }5 \) m3 X" R: p        return &TxOutput{
+ l( k  Q& f' V+ o) Y! V                AssetVersion: 1,$ p" ^: i5 Y; ~/ }) G+ t4 x
                OutputCommitment: OutputCommitment{* E, T& t; O6 l
                        AssetAmount: bc.AssetAmount{
( ]1 z7 J6 I6 D% e/ C                                AssetId: &assetID,
- F3 S2 _7 h( e' G2 U& ]                                Amount:  amount,
) A: L3 y( v7 L- q/ p                        },4 l1 _' N. K; S" a! z9 l
                        VMVersion:      1,: m+ n- u" P2 H# d( N9 V
                        ControlProgram: controlProgram,
; U, y1 Z3 N4 F( o! \                },
  t; c0 X, N1 x. L  Y" q        }! S( ?$ b: A9 l! [. x3 g+ ^1 t5 m
}* _( Q7 ^2 h1 W
5.组合交易的input和output构成交易模板6 B* v+ b2 x; S
通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:- B: f( A1 p' d6 E  b5 J
type InputAndSigInst struct {! J& x6 z3 Q6 w2 x& z
        input *types.TxInput0 M# O$ w( x: r1 N' `5 t9 P4 Y
        sigInst *SigningInstruction
: x6 ~. Q9 i: o* h' p- V% J( E4 B. W}
! u, F7 t. U2 I8 }; E6 }// Build build transactions with template9 ^0 l6 i% X& {! ?$ c& u
func BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {; i) R4 A* i* T: ?; t8 ]
        tpl := &Template{}
3 D4 I# h8 T) }4 L0 L4 D9 a9 G        tx := &types.TxData{}+ L5 H! ?% r* o& g( D
        // Add all the built outputs.7 w; G5 c5 N2 a% C% ^4 w% x
        tx.Outputs = append(tx.Outputs, outputs...)
; Q' a( o( ]0 O# ]9 W# p* U& o" R) Y        // Add all the built inputs and their corresponding signing instructions.
6 r6 \0 f! j* ~        for _, in := range inputs {
2 b6 w" X3 T$ X$ M3 z                // Empty signature arrays should be serialized as empty arrays, not null.
/ e* S$ Z0 P- m: H% K. Z                in.sigInst.Position = uint32(len(inputs))
1 O4 m7 K% {. K" a# O0 Q. l                if in.sigInst.WitnessComponents == nil {" O! w6 v: E# J, W4 l+ c, V2 i
                        in.sigInst.WitnessComponents = []witnessComponent{}: t' _# w# o+ W+ I7 S
                }
1 _' N7 W% h, h4 _8 K                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)! t" V4 r' g) O9 N" K
                tx.Inputs = append(tx.Inputs, in.input)
( u3 x2 V) k" k) P& ]2 V8 s1 g        }
7 r% ^; r% K% P+ T+ Q% U5 d$ p6 R        tpl.Transaction = types.NewTx(*tx)
! T9 b) h7 I: a) V' p3 A        return tpl, tx, nil8 X' M  A2 K' {
}& _  f, R2 c6 q  y1 S1 q5 P! r0 J
6.对构造的交易进行签名
: [6 {( A- [- K) s) O账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)7 p. a, i. E, d7 Z3 z3 h
// Sign will try to sign all the witness
  B2 m. z: e- m8 `& |; j; kfunc Sign(tpl *Template, xprv chainkd.XPrv) error {/ _$ d1 f" m1 C3 ?* B
        for i, sigInst := range tpl.SigningInstructions {
5 z0 v7 J9 j0 j4 H! K                h := tpl.Hash(uint32(i)).Byte32()
1 I3 b* H/ m- I                sig := xprv.Sign(h[:])9 V, c+ g: d& g1 [+ H
                rawTxSig := &RawTxSigWitness{7 K# x  k- z, p  e- A
                        Quorum: 1,9 X* ^6 N6 g$ m4 m8 _+ _6 h
                        Sigs:   []json.HexBytes{sig},9 b! S. Z1 n& ^
                }' W! ]% D  e: ]+ h
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
2 F9 v* E+ x7 W% |0 S4 C0 x. K        }
5 w' G3 R% X! d- N        return materializeWitnesses(tpl)2 _: V4 d5 B3 w6 H
}
: [+ c8 Y+ L7 R" z7.提交交易上链' r; }3 c7 J2 `, j; X
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可
# ^% H; g$ V# ~' n, v! @9 P
- F, n& _+ U4 z, k3 x
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16