Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:3 s8 \1 K0 l4 u. h8 q
Github地址:https://github.com/Bytom/bytom( R$ ?' A- g! E
Gitee地址:https://gitee.com/BytomBlockchain/bytom# H' H. q% L% q  ?3 l4 J" _
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。) u. z6 k( T1 O: D% Q2 B& b
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链
8 S/ b7 E8 H' Z8 l! E: J8 N+ A4 g- y! N+ {& f' H% f
注意事项:+ f% t) [' E! q* Q0 z) r: I: O$ Z
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255$ J) B* d# S% u" p, m
1.创建私钥和公钥# Q2 E. R$ M9 q3 a: ]. d% }
该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥
7 `* `* Y7 w+ o& [' ~) sfunc NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
7 x8 }3 W9 J. U3 `& C6 r  o        xprv, err = NewXPrv(r)& i' p/ x2 l- U! k+ T0 D
        if err != nil {
' v8 t6 S: s3 J0 y                return
0 I9 ?- R( I' k$ n1 k  ~  k        }  U# ]' c# X# T# ]1 e$ c& B" D
        return xprv, xprv.XPub(), nil' V$ Z' q+ ]3 [$ w6 q4 {  b) Y
}
, \- ?2 r1 i/ y# O& E2.根据公钥创建接收对象5 m$ o  Z9 P6 [8 \+ O
接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
& B& S3 E% a) k6 S) A) W1 ]func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
% @, G( R( z) H4 l1 M        pubKey := xpub.PublicKey()
7 m! [, X6 }  ^2 P+ N5 g        pubHash := crypto.Ripemd160(pubKey)$ z, v. w+ @: _8 R
        // TODO: pass different params due to config% M" ]& D3 y" @, ]
        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
! c" Q  w" B/ b0 Y+ t" K  D        if err != nil {
  S$ `% h4 T! \4 x/ y6 f6 L& J8 u                return nil, err
' V  t. b! c- |4 r! b( T        }; W$ s! p+ b- D0 Q
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
. B- t: I# z; s! ^        if err != nil {1 |7 ]! J0 k0 e* R% X3 l, y
                return nil, err$ P# c3 |# H% `, U1 ^9 g* M
        }
, l. I. Y8 r& m8 r7 U0 h4 d        return &CtrlProgram{
0 F6 R8 d# [% K3 p) H                Address:        address.EncodeAddress(),$ Q1 V1 K5 T3 W9 z# w2 [7 ^  G. c; x
                ControlProgram: control,
  g! g/ Z4 m) z# O& U        }, nil/ S; g4 R1 M- n- ^- l2 w
}
2 {/ U. ^1 I! ?& K创建多签地址参考代码account/accounts.go#L294进行相应改造为:
, [( d( R$ _: cfunc (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {; ?+ Z: x3 w  q
        derivedPKs := chainkd.XPubKeys(xpubs)
9 M$ ~0 F4 p& J1 Y        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
5 |# M% Q* h& U+ R. l7 P        if err != nil {
2 G7 f. y  E% H3 o' m+ J4 f                return nil, err( O+ ?8 l8 o1 f, |
        }
/ D  ~  v6 [0 ^% T# w7 e/ A        scriptHash := crypto.Sha256(signScript)7 ?8 p& S, b+ r5 @* O
        // TODO: pass different params due to config4 W1 Z! V' P/ H+ u+ f
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
( K/ R% C6 d& R  _' d4 c% x        if err != nil {0 q( ~% ]0 q! }, L5 a
                return nil, err2 r: U3 [! N) v! d
        }$ J1 V: K: d' e& R. n# T$ f0 Q& {  O
        control, err := vmutil.P2WSHProgram(scriptHash)5 }8 O" f% z( u. m4 c/ k. G+ v& ^
        if err != nil {4 C8 B5 n* L, E9 v' ^) M2 ^0 F6 G
                return nil, err: P$ t1 U! c# h7 i
        }
& q2 H# i# s6 h5 i        return &CtrlProgram{- x3 q2 L5 i! k
                Address:        address.EncodeAddress(),' g- t3 n- ?5 y5 u: e7 U
                ControlProgram: control,( r; b, ^4 H+ k2 w
        }, nil) Z) p4 b( Z( c: l. a7 Y& o
}
% w' Q2 o; h/ ]# o' `5 [4 `3.找到可花费的utxo! @0 T7 v( u" d. i( q
找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)6 ?2 T1 ~1 d& G  I9 H5 I- G$ e4 C
// UTXO describes an individual account utxo.
4 M( G8 U. Q: p" r2 V* U6 ztype UTXO struct {
1 a7 O4 J& u. n/ k( I- N! l        OutputID bc.Hash, T+ B* U/ d2 P9 y8 j% ?
        SourceID bc.Hash
7 `, N8 x1 J2 S7 D) m/ l        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
' w& S/ P# H7 f/ J        // AssetAmount with a nil AssetId.
% m2 f! x# d. w- b, G        AssetID bc.AssetID
/ n; G" P1 x3 l; P+ v5 v        Amount  uint64+ ]/ H5 d  K; i" l
        SourcePos      uint64( q4 ?& b5 g: p" y$ I; {
        ControlProgram []byte1 Z9 w" _# ?9 s- N5 A
        AccountID           string
3 [) y. r6 C/ L' C* J        Address             string- b' E8 H+ c. n( ~! h( ^
        ControlProgramIndex uint64
3 \4 U  d- S( _% ?        ValidHeight         uint64. {" F( n5 f4 u
        Change              bool
4 y' N+ @6 |' F  f}: }# H( |1 e/ ]
涉及utxo构造交易的相关字段说明如下:
4 M" G" ]8 \: A; B0 Z# w( m# |SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址6 o/ j/ T0 K* _) r0 b

- I5 N2 o! V  {; B3 }上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)! d0 D, m6 ~- _1 S9 f
// BlockTx is the tx struct for getBlock func
' l0 J( r/ W* F: Q8 e$ stype BlockTx struct {0 d9 N1 ^) ]1 I- l' K6 [$ [: V; z3 N
        ID         bc.Hash                  `json:"id"`
  r7 J7 Q3 z, _7 S        Version    uint64                   `json:"version"`
0 H; E' g8 ]0 _, ~$ H1 j3 Z        Size       uint64                   `json:"size"`, a/ l& U8 Y& y. O# F" a
        TimeRange  uint64                   `json:"time_range"`  V- E/ n1 Y& C  B# ]. }! c! ?$ d
        Inputs     []*query.AnnotatedInput  `json:"inputs"`
  _% z' \6 N, \4 p; V' P        Outputs    []*query.AnnotatedOutput `json:"outputs"`
6 H& t5 F8 k0 _: `% y& Q        StatusFail bool                     `json:"status_fail"`  y8 L' t- n0 M  d* g
        MuxID      bc.Hash                  `json:"mux_id"`
! w4 |1 l! u0 K1 d2 ~9 N7 o: S}$ |- t$ n: m5 E$ {, b; n" E
//AnnotatedOutput means an annotated transaction output.
: m; @& o( p0 X9 X" \: ptype AnnotatedOutput struct {
2 L/ f" A2 s0 l9 y! f5 q4 J% U        Type            string             `json:"type"`5 S, X. Y' u& `. x6 @+ l
        OutputID        bc.Hash            `json:"id"`
% O* _3 N6 {& k% ^7 Y        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
( [' L. q8 y# _7 W+ |        Position        int                `json:"position"`6 t& l2 Y6 O& x) ~+ g
        AssetID         bc.AssetID         `json:"asset_id"`1 P. B  o! @( q/ M0 ?9 e
        AssetAlias      string             `json:"asset_alias,omitempty"`, G- M  L" q5 x$ ~; v
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
8 Y" Q5 a9 P+ ~, p        Amount          uint64             `json:"amount"`5 e) P: W' [% B0 e' B+ z
        AccountID       string             `json:"account_id,omitempty"`; |) w, q) c* g- Q) w8 e- A
        AccountAlias    string             `json:"account_alias,omitempty"`
- m: S7 B! `$ }  D. Q        ControlProgram  chainjson.HexBytes `json:"control_program"`
% q+ e9 m$ N5 o& ^7 {        Address         string             `json:"address,omitempty"`
& P+ S. A2 g8 U3 w8 d}4 b7 M/ z4 [* k& P7 ?
utxo跟get-block返回结果的字段对应关系如下:
* p! ^3 h) O. n3 ]`SourceID`       - `json:"mux_id"`* L/ k: _2 t8 p, M: d
`AssetID`        - `json:"asset_id"`: [+ k* ^, W6 s1 f1 e. @
`Amount`         - `json:"amount"`& V3 }  o) Z, {) f  P: _
`SourcePos`      - `json:"position"`% i# M" I" v  `
`ControlProgram` - `json:"control_program"`
$ h% q0 }' L4 f: y# W* v`Address`        - `json:"address,omitempty"`* S; T  s2 Z) m$ X/ v6 x4 l
4.通过utxo构造交易. K7 i  T- b9 g" q1 Q# K! m
通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。3 ]; x. B8 F9 Y/ _
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:
2 u$ R% u( G2 d  W9 X  P, a// UtxoToInputs convert an utxo to the txinput
- ?: ]1 Y7 |1 h! k7 kfunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
1 n8 q" R* b  ^9 Y        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
1 a4 H- k& E* b" S        sigInst := &txbuilder.SigningInstruction{}6 c* V, A# W3 e( N
        if u.Address == "" {
8 B0 Y7 m! h9 Y* F$ T9 X                return txInput, sigInst, nil
' `8 w" I3 ^" W9 g8 I3 @/ H9 T% `        }! ]/ `0 t  M8 H7 A  v8 E4 X# p+ N
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)1 z! Z+ U9 H7 x4 i3 G7 y" c2 T
        if err != nil {  V! {/ b' E* O3 ~5 A2 j0 T3 S* c
                return nil, nil, err9 N' W" m" |1 z% K- {
        }! c* T7 o) ^! P/ Y2 a5 y, b
        switch address.(type) {; Y3 B4 y: }+ l( N
        case *common.AddressWitnessPubKeyHash:# [. U; p5 _% f
                derivedPK := xpubs[0].PublicKey()2 |& B, S# G+ I( Q  L9 l( V9 P
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))! a7 O2 n" O6 d2 H2 a
        case *common.AddressWitnessScriptHash:
) z# k5 Z! c2 `. V& T3 Z/ Z$ C                derivedPKs := chainkd.XPubKeys(xpubs)% R: C, {/ r4 U' r, S6 q6 D1 @
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
, ]. i9 \7 G+ ]( _: n6 C2 C                if err != nil {
* H# @0 X& w- d" Y                        return nil, nil, err
6 `! f$ L. v& G. h1 V- z  T( e                }
  c* t- e  q+ Y4 r, O. E8 Y# X                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
' R5 T5 U- |" v  c        default:
! ~9 l- d2 a0 e2 G  D                return nil, nil, errors.New("unsupport address type")& O% V9 N9 D2 B) i; B, }
        }; F! }$ o% {+ E. }) U
        return txInput, sigInst, nil: v+ o2 t+ n. n
}
/ ^2 @. n& X6 F8 [) H  L& p6 w2 J第二步,通过utxo构造交易输出TxOutput
1 u+ N- G; v1 P4 O+ _4 t' H( \/ z该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
4 i- }/ \3 I+ _% u3 B/ H// NewTxOutput create a new output struct4 R! G3 \, c# Y0 n
func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {/ {% l0 f0 I( F. x! p' C7 l
        return &TxOutput{7 v, z# Z1 }: o( `/ Y; W3 X$ G3 P
                AssetVersion: 1,9 b: a# j4 P& k# h1 i- |
                OutputCommitment: OutputCommitment{/ D. }/ m5 F' u9 s
                        AssetAmount: bc.AssetAmount{& ?) h% c- l4 r" A) x2 s& X: v1 G$ \8 P4 h
                                AssetId: &assetID,
! V  }- w5 H3 a/ b                                Amount:  amount,$ }) w# R, t5 D. N- T
                        },( B2 K7 f! f0 ^8 m  \7 V7 z% v  V
                        VMVersion:      1,/ _7 k0 q8 q! e+ ]$ L
                        ControlProgram: controlProgram,1 O+ m) ~% X4 f0 V5 I$ F
                },  E" M2 N" F6 t, [+ s& M
        }
# W7 P; v1 k4 Q' ~' @/ Z6 y}
7 N- y5 ]$ s, Y& A4 h6 E1 Q; k5.组合交易的input和output构成交易模板" u  r; _. M! Z/ z" l
通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
6 {1 u8 H% i5 M/ }9 @6 ?type InputAndSigInst struct {
' [- }$ L0 u  Q) _+ p9 [, G) X4 m        input *types.TxInput6 k. c& K8 F, ]) k& n+ W
        sigInst *SigningInstruction, k7 n" ~( E7 B7 C
}  c" h( y/ z1 r4 o/ h' P% K
// Build build transactions with template. Z$ \5 [6 w$ A9 D' M
func BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {1 Y. m: @: T0 W: R9 |
        tpl := &Template{}
6 l" e7 @/ v' a7 ]# N, f- A& u        tx := &types.TxData{}
0 G7 v5 F4 C5 L* C9 W% w. w: @        // Add all the built outputs.  A/ H8 j' C; [
        tx.Outputs = append(tx.Outputs, outputs...)1 X" x6 E- |$ p- |# P
        // Add all the built inputs and their corresponding signing instructions./ z5 n  x* Q4 M# p: S; i
        for _, in := range inputs {
4 E/ ], m7 ?2 x6 D8 y3 G                // Empty signature arrays should be serialized as empty arrays, not null.7 b! w5 O8 `/ C7 V
                in.sigInst.Position = uint32(len(inputs))
' R0 x9 c0 `; H( D! g  ^( L' s8 \                if in.sigInst.WitnessComponents == nil {
5 M4 h; O4 i5 N3 \; t! \9 R7 f                        in.sigInst.WitnessComponents = []witnessComponent{}6 ~, V% f  D* P
                }1 I1 G0 N9 [* H7 b( D
                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
0 l$ v8 d# v6 o/ L; Q- v                tx.Inputs = append(tx.Inputs, in.input). [7 v6 W' G" q9 ~: ^8 g
        }( H) q) Z% A5 j8 }. h9 _
        tpl.Transaction = types.NewTx(*tx)
. k3 r: f" Q  @0 l        return tpl, tx, nil
& H; l, a' Q7 H& S}; o: P9 `. G, R/ b% ]9 v* {
6.对构造的交易进行签名) g/ n  u$ O5 @/ y% w  u* y
账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)
) s; K" Q, R: I6 w4 u// Sign will try to sign all the witness7 H( U. s3 c. G% R
func Sign(tpl *Template, xprv chainkd.XPrv) error {5 X$ N9 _7 G* F+ d" j; H, [
        for i, sigInst := range tpl.SigningInstructions {2 [1 _3 ]" P1 x: r
                h := tpl.Hash(uint32(i)).Byte32()
3 E+ c# V( }3 N. b                sig := xprv.Sign(h[:])9 l# e" A- c+ t9 F6 o
                rawTxSig := &RawTxSigWitness{1 T+ I+ K+ T1 y, |# b! u) w
                        Quorum: 1,
$ t' W+ v: J3 D. O& U& L) ]8 T                        Sigs:   []json.HexBytes{sig},' V9 W8 m4 j/ x  |8 r
                }
& Y8 Q' }+ a/ d3 j                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)2 `5 Y& L" d$ e4 J& Y
        }
) C3 H" h6 i, l. f) _: t9 f        return materializeWitnesses(tpl)
6 J5 |4 A9 a; G  d0 C. P" o9 E}
" g" [- K3 A8 V1 @3 n3 [7.提交交易上链% z3 @/ Z4 I$ A2 _% ], I# ^
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可$ x4 w( i# m3 q: j9 r( I4 C$ r

- u) }* o" A1 l6 C) ^1 L5 r: a4 E
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16