Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:2 C+ x! e$ U9 a  \" V' c9 q) m
Github地址:https://github.com/Bytom/bytom
( O$ m4 ^4 ?; }$ GGitee地址:https://gitee.com/BytomBlockchain/bytom  l3 R3 N) s. D4 w
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。
) k. e3 `/ i! \8 r0 S1 ^& t" t1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链# b$ X& f3 k  M0 r+ P: H* O: N
% R, I7 Y2 P( i' }
注意事项:
( e' J2 a- j; R4 a& d* B: p/ W以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
" |# h+ k% o. |2 s" p6 E2 P/ E1.创建私钥和公钥
' o0 Y1 y% k  e! N2 b该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥& N# Z, h6 l3 r. M( T0 z
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {  b, X! h% K2 K" \
        xprv, err = NewXPrv(r), b+ [$ q( n* A- {
        if err != nil {
9 b, a6 m4 H$ \) s) J3 y8 V. c                return
0 X. w; G# z1 O; {9 g* q        }
- r+ f) a" S. k* ^% y# \0 F        return xprv, xprv.XPub(), nil: L. I( o$ w# F
}" u1 F; Z4 T' G7 j# Z/ N4 B
2.根据公钥创建接收对象: L' ^7 |  y8 s7 }
接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
9 b9 H. X5 z6 q) [" O' qfunc (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {2 ^( V: A3 H6 L: m1 j+ Z
        pubKey := xpub.PublicKey()
' _# ?0 e; c' S        pubHash := crypto.Ripemd160(pubKey)( p! W1 _$ N9 A' {+ k
        // TODO: pass different params due to config5 l. T, A- _" v: v7 j
        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
" M" _5 I* Z: ^# B' B/ \7 x3 ~        if err != nil {
' C. t+ S& s3 G, D! m8 @" W                return nil, err
2 L5 z2 w2 u9 E& p- L6 ^, }. X# {        }
3 m  E. P& K8 ~1 W% {        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
! ^& J: _& g. y6 e0 j# Q        if err != nil {8 r% U- t9 v& d. L5 x4 B3 B! \( G6 ]
                return nil, err
$ H$ b5 j" N6 w, U  z( ]        }
+ h/ d/ P! w3 A* E& n        return &CtrlProgram{, |, q, C$ r# O; T: e
                Address:        address.EncodeAddress(),1 C+ r7 S+ y/ k8 e
                ControlProgram: control,
( g) ~; i/ |! D  B; X$ [        }, nil7 Z% ]3 Y# Y. N
}
3 l& N- D' O9 ?- l- @% \创建多签地址参考代码account/accounts.go#L294进行相应改造为:
7 @0 t# Y) o1 k: ?% z" D& Tfunc (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {7 X& L4 G+ r8 B' I2 Y1 L5 ?5 r7 d
        derivedPKs := chainkd.XPubKeys(xpubs)( _" s2 c* w, v5 v- j
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
9 s0 A! e/ \4 N9 t- e. L* p1 y        if err != nil {
" g; y4 }) ?5 p& ]" }: j                return nil, err
% r& ]$ g& o1 j/ S3 ~( y        }
. C  ~* c% ]2 U* S/ d        scriptHash := crypto.Sha256(signScript)3 T1 g  w& F" d3 M
        // TODO: pass different params due to config+ r5 F  c. B. Z: G& ^# q
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
; z2 i. G6 v! O+ @& {        if err != nil {! I. C% Z  O0 d* s  d: H5 O
                return nil, err" E! X" h" A  j- h
        }" S, S) O+ `3 c' n" @6 _; j
        control, err := vmutil.P2WSHProgram(scriptHash)
/ K, x/ h; p+ p' f5 m% F5 O8 k        if err != nil {
; f$ ]4 {5 D4 E  t4 N8 H                return nil, err5 F+ Q. s7 Q3 n, d" Y5 u: g' q
        }
! g; h9 t* k% U1 w        return &CtrlProgram{
. D+ V$ w  y' X" v  S9 T+ S                Address:        address.EncodeAddress(),/ b! X) R# w; s) V8 y
                ControlProgram: control,$ R4 l: ?4 W. ~: @8 t
        }, nil8 v% k0 d9 x! g: v7 \2 {
}
1 k; W) R8 L+ N6 R6 t3.找到可花费的utxo
: G7 T3 L, L( o! \3 J8 r找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)5 _! q" J$ Z/ j
// UTXO describes an individual account utxo.2 B: `( f% R2 Y* f* g: `
type UTXO struct {- y0 a9 V, e$ V7 t7 k0 Q. R
        OutputID bc.Hash
& ?; T# F* i2 x; S$ j        SourceID bc.Hash% U7 x/ U/ M9 p* Q7 A% j
        // Avoiding AssetAmount here so that new(utxo) doesn't produce an& O0 B& B* e9 L  ^
        // AssetAmount with a nil AssetId.1 {/ s$ @& P* G4 h5 s. V
        AssetID bc.AssetID8 d6 c0 _& P. [, p8 |
        Amount  uint64
$ T" f; L/ F; v; Y        SourcePos      uint64
$ U" D- X, M0 N: K! @5 f        ControlProgram []byte9 ?; ?$ `/ k( K/ Y; X
        AccountID           string
% B7 |9 C* D- _- Z        Address             string6 z# B0 k+ T# a  z  v5 |7 r; a
        ControlProgramIndex uint64
, s1 n" B. i  \: q& G        ValidHeight         uint64
% E0 }' Z( ?0 u        Change              bool2 q# ~, J  `/ l4 }3 H
}+ a# }. d4 h# |
涉及utxo构造交易的相关字段说明如下:4 T/ c' |/ m( J+ q9 h$ v
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
* _0 y* x8 Z) g9 J4 m9 j4 }8 o) F! T6 D* a" ^
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)
' S1 n" N- s/ Y& v( B// BlockTx is the tx struct for getBlock func
* O5 R, E# m! Y3 ?1 P% wtype BlockTx struct {
. K# i' ]+ |1 D        ID         bc.Hash                  `json:"id"`
2 L* ?5 X7 Z/ k        Version    uint64                   `json:"version"`
% v7 W! |( j9 e+ W        Size       uint64                   `json:"size"`/ t! H, o/ p( p, {: [
        TimeRange  uint64                   `json:"time_range"`/ A0 y7 d8 c7 h; O  Q& Z5 V
        Inputs     []*query.AnnotatedInput  `json:"inputs"`1 ?  {' v, Z0 B
        Outputs    []*query.AnnotatedOutput `json:"outputs"`
+ V3 ?: {( p; j) ?+ b9 T        StatusFail bool                     `json:"status_fail"`9 S1 t, `3 Q' s5 _$ B0 Y9 ?
        MuxID      bc.Hash                  `json:"mux_id"`6 j$ f- R: C% k& @: r
}0 \# [4 I* V) ]$ x8 n5 y) @
//AnnotatedOutput means an annotated transaction output.. g5 a% }) C- t8 E9 K# X; u* n+ {
type AnnotatedOutput struct {* a) y! q  j1 R* Y4 T$ s
        Type            string             `json:"type"`9 s+ F9 v; \/ |" X% `
        OutputID        bc.Hash            `json:"id"`
. T0 f' X5 @" P5 s        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`, W  `; A5 m0 n
        Position        int                `json:"position"`
  y/ ]$ r( o! o1 X- h6 L        AssetID         bc.AssetID         `json:"asset_id"`& U: L# o1 v' M$ o3 d! |
        AssetAlias      string             `json:"asset_alias,omitempty"`
8 S# t# K' V& e; s1 K( P        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`! ]# C/ @% |. ^+ c8 s- x
        Amount          uint64             `json:"amount"`
) j1 Z/ T3 P9 Y1 z        AccountID       string             `json:"account_id,omitempty"`# Y- @, m' R5 _4 y2 D
        AccountAlias    string             `json:"account_alias,omitempty"`) O! |) C4 n! C
        ControlProgram  chainjson.HexBytes `json:"control_program"`
" `9 e' e) ?" q* N        Address         string             `json:"address,omitempty"`
( [5 e& p8 o4 ~3 w. c1 T}
( {6 k& F4 Z8 I" t" ~utxo跟get-block返回结果的字段对应关系如下:
" Z" \- P6 [1 n% m; F`SourceID`       - `json:"mux_id"`# s  p9 `8 C6 a( [+ V9 U5 I
`AssetID`        - `json:"asset_id"`5 |% ^+ @# G; A
`Amount`         - `json:"amount"`7 S. z/ m& K: E0 K
`SourcePos`      - `json:"position"`
5 W3 r( w! G) T' S( F/ K`ControlProgram` - `json:"control_program"`
, f2 Y' N0 f8 N* F5 T* m* ^`Address`        - `json:"address,omitempty"`
) q6 J& j6 v4 d0 ]6 S4.通过utxo构造交易
7 a! x$ U: M. M% p通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。( y2 q" h" y0 ?/ _4 T
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:+ S( q" N" j6 F$ i# y2 L3 ^- a
// UtxoToInputs convert an utxo to the txinput; @3 B) V! g: A3 ?9 ?$ |# ~4 j: M
func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
7 I8 x# `% m# N+ J- p1 f        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
+ |& A! C: R- Y7 z        sigInst := &txbuilder.SigningInstruction{}- t- Q7 ~) ?0 t( G! r* [' ?
        if u.Address == "" {
8 Y8 s  [' c! a  j9 A% a                return txInput, sigInst, nil
+ g0 H+ Q7 z9 U        }  `+ s6 Y2 C+ J* a
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)! R1 F, e8 }# e3 l+ n, e' J! d
        if err != nil {
. i0 K+ Y8 L" ?  m                return nil, nil, err
! ~  f+ J! ~9 H, d' ~  C        }0 }6 R) S! c. g! a* t5 v
        switch address.(type) {
8 [" [7 r$ Z+ v& E        case *common.AddressWitnessPubKeyHash:" c1 V* {3 d5 K$ L, w+ E6 g
                derivedPK := xpubs[0].PublicKey()4 t4 z! T- h, z' D- F# |
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))5 Y% o+ j/ m" N- }& q: q5 g
        case *common.AddressWitnessScriptHash:) u* M! U) ~: B  W$ D
                derivedPKs := chainkd.XPubKeys(xpubs)( U) ~1 V1 N6 |& H
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
( G' `! E4 d* H. W, A3 ^- Q                if err != nil {8 C$ l% d, j9 e. g: T: l
                        return nil, nil, err( b7 R* E* k0 i4 ?' ^
                }. j9 s# [: s" J, T5 Q
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
; y* d, q! }: L4 x        default:
! Q1 `2 ~& O/ K5 m$ y6 f, d                return nil, nil, errors.New("unsupport address type")
3 b/ @% a* [/ a        }; N8 I/ O+ B) u. ~8 q& g, P& c
        return txInput, sigInst, nil+ E$ R$ H/ x+ Z3 p2 w# R& k
}0 b% H/ i& `8 r* n2 x( ~. _# H
第二步,通过utxo构造交易输出TxOutput
* `- Q/ o; U+ m7 V7 P: X该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
( g* l* D! Y' `/ Q+ e// NewTxOutput create a new output struct
1 U# o* |  w: y4 J! i6 T. ofunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {" c2 c' i) Y( _7 ^
        return &TxOutput{( l" F. o' C) m! K3 Y/ B, {$ @* z
                AssetVersion: 1,! B; Y: O; h# B9 r
                OutputCommitment: OutputCommitment{
6 d% e! N8 k' i# r9 l                        AssetAmount: bc.AssetAmount{0 _1 F. K6 ?/ x, U+ @. `& g: J: y$ H
                                AssetId: &assetID,8 g+ q& ^4 B1 j
                                Amount:  amount,
8 S3 r( E$ b. U& I* o' j                        },
( T5 h9 d; E4 |$ ?                        VMVersion:      1,( {0 r4 p! p" q# L$ G' {( r
                        ControlProgram: controlProgram,( s9 I& g6 r# N. d# _
                },1 ~+ l  D" w9 D2 x# Q4 I
        }
( J' S$ M& y3 ?: r: a" f}. E. y7 r) ?: P  P
5.组合交易的input和output构成交易模板
7 U* f- J' W2 T) Q# V2 s通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:, A2 \; S8 x. i$ i( T
type InputAndSigInst struct {$ b, H' g! `$ }% j; X& H  }) M; u
        input *types.TxInput, ]; `' c0 M& z
        sigInst *SigningInstruction$ c6 e) U. p2 b
}
, l1 i  U* H, g- Z! r  W" g// Build build transactions with template
% b' o1 z+ B; s  o/ `! @& Jfunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {( t" B& N: i6 @2 ?4 ^7 w2 ]
        tpl := &Template{}
" j* L* \) [7 G0 _8 ~/ c& f' S        tx := &types.TxData{}* a  F, O0 }- x8 i: g& K+ W
        // Add all the built outputs.9 E1 [+ U/ R+ g. |/ L: f3 |
        tx.Outputs = append(tx.Outputs, outputs...)
" U, p8 t" s" H( j        // Add all the built inputs and their corresponding signing instructions.1 b, \6 V# Z8 g7 T  X. O. H
        for _, in := range inputs {. F4 Y' G/ R2 @1 D1 V2 _* R
                // Empty signature arrays should be serialized as empty arrays, not null.
7 @. R) L; k# W; b/ B/ T                in.sigInst.Position = uint32(len(inputs))* j, L+ \/ |; I; j
                if in.sigInst.WitnessComponents == nil {
+ _% L9 q+ S+ W  c# B                        in.sigInst.WitnessComponents = []witnessComponent{}
- z) |. ~, {% @, `" D3 }                }
# d% Z) v& M6 m                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)7 q# J4 [$ W7 \2 w
                tx.Inputs = append(tx.Inputs, in.input), N: d9 v5 n  y8 a, a
        }
% H5 t8 u! l# Q. f+ t9 J        tpl.Transaction = types.NewTx(*tx)# {" s) E& d& [* B5 i: B  g7 \
        return tpl, tx, nil6 J8 u( {' |! r( Z. ^5 u
}
- I' z1 O6 e* Q6.对构造的交易进行签名5 j4 T. o+ c) ^5 H+ k
账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)! |8 A  f# H& f/ d; U
// Sign will try to sign all the witness
1 R  }+ B; L/ Q4 Y+ U/ F4 M  |func Sign(tpl *Template, xprv chainkd.XPrv) error {
* g; q$ q; {1 Z- g2 \        for i, sigInst := range tpl.SigningInstructions {; n* j5 C- ?; X  v
                h := tpl.Hash(uint32(i)).Byte32()  h# D" P. u1 w% V& Z( m& r" R
                sig := xprv.Sign(h[:])8 t4 T& H- Y2 B
                rawTxSig := &RawTxSigWitness{5 _9 j' `0 X5 T9 \" V; ]
                        Quorum: 1,
1 U, _9 c- g# j                        Sigs:   []json.HexBytes{sig},
6 P0 o1 h, t! H+ }* m) v) p, G                }& D- ^: p. E9 q' Y% R+ f; s
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)9 [- N0 x/ r' U* v" ]
        }
  m7 Y2 l# N0 [1 [& |2 A, h        return materializeWitnesses(tpl); r: K+ x% X- W; I! ]! @" D* k
}5 v) g" z2 I+ U, ?! F: Y; W& c  A
7.提交交易上链
5 {; y# C9 `6 @+ N6 A+ a7 M该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可) {8 s. k, s& z

- u7 m8 ?6 \+ Y$ m1 g0 @% L
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16