Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
1 c$ L# L( C) a0 l; @8 y- @2 yGithub地址:https://github.com/Bytom/bytom
6 ^8 u& f8 r4 S; o' c' i- qGitee地址:https://gitee.com/BytomBlockchain/bytom
: ~; e( P9 B8 @' g( h# [该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。# ^" I: R- }' f+ y: Z
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链
4 \& j% z' I: D8 z& X, X+ F7 o2 W2 z7 @. a: G9 L
注意事项:
. s3 u# {4 {  J" S; R以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
) B0 p) N1 P* T1.创建私钥和公钥
: }6 E- N. z6 s' p5 K8 I  c该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥' s8 Q# E& b3 D  f
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {& n. R1 a4 L  [! F* V
        xprv, err = NewXPrv(r)
$ M2 u( W: ^' I3 W; I        if err != nil {, J+ x) K) z% Y' x
                return! X9 j: m. ^3 {8 p9 l7 R
        }0 d; `4 |. m8 s% X, f
        return xprv, xprv.XPub(), nil
8 W' d6 B) v3 L  C0 w}& O' S5 H/ w) {0 d: a5 X
2.根据公钥创建接收对象
% N% ~$ R8 X, g/ E' L# S( @7 ^接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:' V+ e$ ]; b5 P5 e% n" W8 T8 v
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
' D: j# \) n, ^: g, |$ Y        pubKey := xpub.PublicKey(). T3 K9 Q9 ]3 i" M( ?; d
        pubHash := crypto.Ripemd160(pubKey)
; j( X8 s- F8 Q# R6 ?        // TODO: pass different params due to config
) ]/ G/ ~6 X) B; Y, l  t        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)  `5 [4 w' U4 v
        if err != nil {4 u& T7 l+ z+ P+ @
                return nil, err
- |2 A! L6 ]/ C- p( ^7 d        }! J  m; E- F* X. U8 H4 o; ~/ V
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))# S' O3 o9 R& F
        if err != nil {
/ X/ h& I) q. c3 Q5 D                return nil, err
+ d  l) X: m6 ]        }' Q$ h/ Y0 g) q. L+ C
        return &CtrlProgram{' [/ G( g! F# \) x- D6 T: l5 r* h
                Address:        address.EncodeAddress(),9 R+ ~& ]9 G0 A% k
                ControlProgram: control,+ s1 r5 B' Q# Q9 g3 J2 @
        }, nil1 r' T; c) g/ V# U$ o: J% O
}5 {; _8 R# B+ v, C4 W
创建多签地址参考代码account/accounts.go#L294进行相应改造为:
5 ?& a' N' ^: O( Ufunc (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {+ ]5 W1 e" r* p- a3 A+ }
        derivedPKs := chainkd.XPubKeys(xpubs)
/ q0 X2 [* M" {% `, B# s" l        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))" C+ R. X2 v) n' e) C9 j
        if err != nil {' Y8 t- Z/ |8 ?$ L& M
                return nil, err0 m2 }- p9 k# y, l
        }
- Q: d: @' U& V& V* T+ @        scriptHash := crypto.Sha256(signScript)
1 t% Y* N7 U6 h9 F        // TODO: pass different params due to config' I6 ^* K) ~6 l; }0 y2 {
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams). q! W9 V! Z' ?0 Y1 J1 C# i- G
        if err != nil {
7 W0 V8 g8 j! ?1 D3 f) J                return nil, err# f) N! Y* _5 d3 w
        }8 X2 e0 j4 p/ }/ o/ F( d1 r' q! k6 L
        control, err := vmutil.P2WSHProgram(scriptHash)' w% b* z4 I1 _3 t
        if err != nil {1 ~$ [, ~6 e5 `8 G9 a
                return nil, err
/ p+ U. a" j: J; C        }9 a( F% T# z0 m/ g6 U, O
        return &CtrlProgram{
/ d/ i7 }  K9 `: F/ h  U                Address:        address.EncodeAddress(),! q$ F  |. E5 Q5 }. N4 v2 v' n
                ControlProgram: control,2 s- Q) N$ V+ t& E6 }7 {+ a
        }, nil+ G/ ~" O7 ]# S4 t
}
- m2 E. W. l  t3.找到可花费的utxo' L2 m8 n7 G7 Y' R4 u" V  k
找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)6 j6 z) E7 M" k( e5 L. P
// UTXO describes an individual account utxo.& q3 ?2 H$ m( G' c3 `9 C. M
type UTXO struct {
3 t3 r5 f1 _$ @* k        OutputID bc.Hash0 }$ x$ m2 [6 q
        SourceID bc.Hash4 L6 b( z/ r& C8 _' d+ F
        // Avoiding AssetAmount here so that new(utxo) doesn't produce an  c* Y3 `" X! T/ F, V
        // AssetAmount with a nil AssetId.+ V7 U! H( l# K' |% p# N% \
        AssetID bc.AssetID
2 R/ I! S) R0 C        Amount  uint646 a& O) c1 I  s1 Z
        SourcePos      uint64
3 H1 O) O. d- K" x        ControlProgram []byte% q0 G/ ^. \) P
        AccountID           string  ?% u6 I3 X- p$ m" {4 Q& T8 y
        Address             string1 c6 L; V) x" w$ c, e2 f- S/ D0 d
        ControlProgramIndex uint64; D6 F3 W8 o! N
        ValidHeight         uint643 \- Y. q1 W8 }  ]
        Change              bool
# ]5 ?# w7 x4 j7 u) [( p' Z( T}, \4 v- g# G3 d$ S
涉及utxo构造交易的相关字段说明如下:
/ ~: [/ V4 p: P, ?- PSourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
( {( H  s9 ^; S# e" J0 A- o3 {- Y; l5 V, F: B/ ^
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)
! [3 ?: ~* Y, _0 V( T. E// BlockTx is the tx struct for getBlock func
- ?: D- Q8 j* D7 S4 ^  qtype BlockTx struct {* @+ ?  h7 C6 G! L! @9 E$ j: w- F- q1 n
        ID         bc.Hash                  `json:"id"`" {* l) s/ B& v+ ?
        Version    uint64                   `json:"version"`
7 a. @6 N+ K# I+ F3 D* s        Size       uint64                   `json:"size"`
$ ]; c( e; l$ j7 D2 g1 _5 H        TimeRange  uint64                   `json:"time_range"`
; d1 ]- c5 J0 e3 ^% |" D        Inputs     []*query.AnnotatedInput  `json:"inputs"`
2 ^# J" v1 F" w$ x/ W8 H" i        Outputs    []*query.AnnotatedOutput `json:"outputs"`. d6 W; l' l' f
        StatusFail bool                     `json:"status_fail"`
6 t' O! j' y% T, V& Z8 N        MuxID      bc.Hash                  `json:"mux_id"`1 `1 V* {) `0 M( i
}
! Y8 W! ?$ i# `; G, z2 ?4 o//AnnotatedOutput means an annotated transaction output.  B( x6 k4 P$ D7 [7 F
type AnnotatedOutput struct {  Y9 P' v" }4 f$ ^4 p& ^# J
        Type            string             `json:"type"`
# j4 ~7 x, L  |" K' K7 p$ C        OutputID        bc.Hash            `json:"id"`2 j! G4 G  W8 u' G* A
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
) {6 c( H+ P* C        Position        int                `json:"position"`
4 s' y  s. x4 }        AssetID         bc.AssetID         `json:"asset_id"`3 S; m. Y. x0 p( y! d
        AssetAlias      string             `json:"asset_alias,omitempty"`( r$ K$ B# D2 i& p
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`$ n8 L3 g4 m, l; r6 O0 R
        Amount          uint64             `json:"amount"`# [8 Q4 r2 Q$ T/ g! z/ m
        AccountID       string             `json:"account_id,omitempty"`
4 h9 e% x* g& X2 K- Q8 E' H        AccountAlias    string             `json:"account_alias,omitempty"`
% w) |' H: N/ s$ `" w0 e6 r        ControlProgram  chainjson.HexBytes `json:"control_program"`
0 B3 o' T! L+ |        Address         string             `json:"address,omitempty"`
  U2 R% e3 A  h7 Z+ I* i; s- k}( X' S) `( U. N* K, D0 {
utxo跟get-block返回结果的字段对应关系如下:
0 H" x. m4 q" k% Z/ G' }# b`SourceID`       - `json:"mux_id"`9 T* ]( D" ~5 k/ S/ K
`AssetID`        - `json:"asset_id"`" V. g9 A. s  R$ n: Y
`Amount`         - `json:"amount"`
) D4 e  B' T" s7 _`SourcePos`      - `json:"position"`! B9 g; ^6 r% U) W
`ControlProgram` - `json:"control_program"`
; o0 b# B0 |2 K, r9 [`Address`        - `json:"address,omitempty"`6 W5 m1 y' L& `+ i  ?2 k1 j
4.通过utxo构造交易
- o) l0 g1 s9 }+ U- Z5 ~5 A通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。& |, {5 M2 v0 q; k3 ~" o: B
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:9 {9 y" Y7 b* z1 L5 L6 l! X* q
// UtxoToInputs convert an utxo to the txinput
- G( l. g0 G. ^: p3 G7 x% h0 Cfunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
. G1 B4 n# t3 Q        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)) `. N+ l2 [, z/ K& s
        sigInst := &txbuilder.SigningInstruction{}- y2 S9 T# K: M
        if u.Address == "" {( e5 P) P; a+ r+ S" ]6 p- l
                return txInput, sigInst, nil  `) K  ~( `/ H5 ^* H
        }" r) l' Q8 {/ W
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)9 i& Z8 s# v% ?9 z! F  B# s7 [
        if err != nil {
( J6 e! _$ D$ T  d9 N  b9 M" N; ~                return nil, nil, err7 S: {9 y" v  g8 o) |
        }( A( }4 k4 D# ^. ?/ v7 _3 R
        switch address.(type) {
8 f( n6 n2 @4 Q* R- f        case *common.AddressWitnessPubKeyHash:
: X1 ^- m8 `  f. R                derivedPK := xpubs[0].PublicKey()3 Q! h! B9 v9 p# q
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
9 `; }4 P* F3 r7 D$ Y# K- I        case *common.AddressWitnessScriptHash:
4 h$ T2 Y4 G  |  s                derivedPKs := chainkd.XPubKeys(xpubs)8 G( w1 ^& ]( F2 C6 ]- J
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
, k  u! ^3 Z& \( j6 e, ]0 @( z                if err != nil {9 C  O* V* [0 V3 Y4 O
                        return nil, nil, err$ h1 j/ ~: k9 j: b9 M! G
                }0 q. A! U3 t" j0 Z, [- E
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
4 K1 k; M% K* O9 u) P8 `5 N        default:
7 ?: r, [0 y2 F* w& B                return nil, nil, errors.New("unsupport address type")1 J& }  l' y$ N: u4 \2 Z9 p
        }. P+ \+ I7 b1 y% c) ^7 z4 L4 E7 p1 ^
        return txInput, sigInst, nil
+ J5 y1 o4 O; t- v+ R: C}
6 F! W* M- g8 {" ?6 D第二步,通过utxo构造交易输出TxOutput4 N9 w: Y0 B/ i+ j
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:0 d+ f/ J# R8 o$ E
// NewTxOutput create a new output struct
9 U" ^; s3 n% i! r4 T5 X- Efunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {6 N# x; M) j3 `! Y3 {/ m  @
        return &TxOutput{0 w2 z, G/ `1 h7 a6 F+ Y: w
                AssetVersion: 1,
- u3 i6 c  ~. o1 t2 C+ C' a* j: W                OutputCommitment: OutputCommitment{
# S) M5 K$ S* n/ G' v- w                        AssetAmount: bc.AssetAmount{0 ]6 L* w$ _) o) `; e9 x1 Y1 B* M. h
                                AssetId: &assetID,) l- F( ^& o8 @" ]* a6 Q
                                Amount:  amount,
8 j1 {6 H; }6 |2 ^                        },4 |* i1 H/ M8 j" \* L
                        VMVersion:      1,
5 B# ]' `9 m5 I; Q* h- q                        ControlProgram: controlProgram," N9 Q+ u! P) z! i% M! }1 j* m5 x* s
                },+ d4 ~4 V2 h3 T4 m8 o- c9 a9 @
        }$ N; W6 F/ e1 l9 Z/ y: p
}
3 t$ N  D- i& L3 Z( T8 {8 W5.组合交易的input和output构成交易模板
9 E. N7 Z9 w; D" O; i通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:0 u6 B/ }7 E' U; B6 c4 D
type InputAndSigInst struct {
  {  f7 U: {  U. k2 v. |. X        input *types.TxInput
5 j0 ~4 Z' }2 \! e( m" t4 f        sigInst *SigningInstruction( {5 `" r; m! p* `. g
}
/ g6 p  G: I6 g! \" I& N2 L& j8 J// Build build transactions with template
$ |% f+ c3 B3 \/ B5 N( Bfunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {0 G7 `( l) q- E6 w) Z
        tpl := &Template{}1 r2 x, K9 v+ b3 }( @
        tx := &types.TxData{}6 `- w; i8 P! f/ n: u9 e
        // Add all the built outputs.+ I; O- s9 c* B" V9 G7 [" P
        tx.Outputs = append(tx.Outputs, outputs...)
' G% x  h1 f. }$ c+ e) a        // Add all the built inputs and their corresponding signing instructions.) p/ q; c, M1 ^" \+ j, [6 x( Q
        for _, in := range inputs {5 V+ g. C, m. V
                // Empty signature arrays should be serialized as empty arrays, not null.- Y" D. Y& w! a. R, n* E& e
                in.sigInst.Position = uint32(len(inputs))5 ]0 z% Z! Q5 T/ t/ n
                if in.sigInst.WitnessComponents == nil {  `1 z9 B0 b; A# n7 Q
                        in.sigInst.WitnessComponents = []witnessComponent{}  S! k+ i% y% J& p: y
                }
6 ?6 T" v+ S0 v5 g! b" c                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst), |6 s) ~! g1 D8 G+ P
                tx.Inputs = append(tx.Inputs, in.input)  v/ X# V2 ]. _8 K
        }& S* g; y3 }, H) M; x
        tpl.Transaction = types.NewTx(*tx)
" k% K. s5 Q# O  o9 l; b) |0 f        return tpl, tx, nil$ r% b% \% m$ V& J( n6 e6 [: |
}
8 X  K8 B6 t6 S( J! g+ W6.对构造的交易进行签名- p9 `; ]5 r2 V" K
账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)0 ^" ?1 y3 m) @3 H5 p
// Sign will try to sign all the witness: g% z* K. p' E' S5 y
func Sign(tpl *Template, xprv chainkd.XPrv) error {2 d5 }$ {4 b; D
        for i, sigInst := range tpl.SigningInstructions {
" G- v: K3 g# N9 K                h := tpl.Hash(uint32(i)).Byte32()) w( X( i) E; s- i# N" K& f$ i
                sig := xprv.Sign(h[:])
( V* Q% ?' J/ r* U* R: w7 n                rawTxSig := &RawTxSigWitness{
" N: \) i) t3 n5 A& z                        Quorum: 1,! X2 X5 h5 ~9 t* z  v3 v$ h5 Y
                        Sigs:   []json.HexBytes{sig},
5 L. x9 L" g% U$ r- \# D                }) {+ u! r! u, }7 Q% Q2 }
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)6 O: n: v) x8 \
        }  q3 L5 W  ^! V
        return materializeWitnesses(tpl)
" |9 o( Y6 Q' w- z}* ], d3 k! k' i4 t
7.提交交易上链) e0 w+ B* I& O. p( N
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可5 q  t( \& r; Y2 w  ^) Z

7 ]) H  \7 ~+ h& q: m6 B+ @8 v
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16