Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
  q) Z" _4 ]6 E1 k, _5 dGithub地址:https://github.com/Bytom/bytom8 v" t# ]( n6 a9 v; [5 ]
Gitee地址:https://gitee.com/BytomBlockchain/bytom
6 A7 |% f+ i6 }) n该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。
4 f4 ]5 m, M9 _4 q: K- n% ~) p1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链8 Y9 F! p5 Q8 N5 `

" B9 \/ n2 n% s$ y& x注意事项:6 W2 ?5 ~0 j, w! d  U, r
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L2552 T0 K3 |. Q" B2 }2 a
1.创建私钥和公钥
2 n6 X* q5 F$ W  }" p/ [& ]6 U该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥
% A0 h" Z! H* W8 J. ffunc NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
( V3 J* A6 s9 {8 n5 `        xprv, err = NewXPrv(r): ]: @# g1 x: ]5 X! ]
        if err != nil {
( S3 b% w7 ]- f) k) y: R                return; Q3 J9 R0 y4 w" H- n
        }
8 h2 a9 `  w  z) l) f( |        return xprv, xprv.XPub(), nil
3 X: O8 v( c, H2 C$ L, O3 V8 Z- X}2 u5 b# Q3 a& y, P  t' T* o6 V: @
2.根据公钥创建接收对象: b5 Z* q: M7 W
接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:( n1 h; H3 `# f& c+ m, l7 g
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {2 e1 Y; d0 N' i8 R. O! t
        pubKey := xpub.PublicKey()+ F# X" r) c" z$ _1 J0 ]  ~
        pubHash := crypto.Ripemd160(pubKey)7 n, W2 L/ l/ {  ?! l
        // TODO: pass different params due to config
4 m( w' P  h9 z5 i5 I4 X# a7 X: p% N5 {  |        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)6 l) l- g! w3 L
        if err != nil {
. l0 E+ i: X9 Q( {                return nil, err  Y1 F9 i) ]5 A9 g
        }2 N3 \' u1 ]* _0 x5 C" @
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
6 p- [0 M5 j+ v' U        if err != nil {
8 X3 m5 j0 h* N3 k+ Z# i8 f                return nil, err
9 |" ~& w) R! r        }5 O! A9 I7 H3 N& Q# _; y/ |
        return &CtrlProgram{
  E/ R1 u; s2 {# ^5 j% m- _2 s                Address:        address.EncodeAddress(),
7 z7 X8 D, A% A, Z8 T( L                ControlProgram: control,
6 q, _/ _0 F8 \        }, nil; z" R( S9 M( X: Q
}8 c( M- B- X1 n9 a; @/ F+ Y/ G! B- p
创建多签地址参考代码account/accounts.go#L294进行相应改造为:  m  D( o# c/ q* e
func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {" y( U9 k2 J! R0 z& T0 D
        derivedPKs := chainkd.XPubKeys(xpubs)5 }; S( z3 s, I: ^( |; `
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
% m- ]7 Z/ Y3 \6 S1 `5 H        if err != nil {
  h# S& F" R  K" t. ^! C& u" [                return nil, err0 D2 \" ^8 n- `1 h2 d$ N
        }# e3 w7 d) f( D, C
        scriptHash := crypto.Sha256(signScript)1 V: G1 B! q: _) b3 z
        // TODO: pass different params due to config
) a, T$ K5 \+ H! @        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams); s9 I2 K$ y) U1 D" e, H
        if err != nil {
( m+ F# r% U8 Q* G5 }8 Z                return nil, err
0 j2 n- F! y" D1 a, T4 h        }' @4 F0 l# y* k2 V; v1 b
        control, err := vmutil.P2WSHProgram(scriptHash)* o& c+ `/ i/ U7 e' }
        if err != nil {
% A; @$ _& l, L" H6 s- H                return nil, err, F: Z) R! k0 L4 ]
        }2 D( Q6 L9 X& q3 v
        return &CtrlProgram{
$ p0 `0 x/ ]" |% w# b' B1 N) c: A                Address:        address.EncodeAddress(),% d% d4 T+ l9 M
                ControlProgram: control,* W. P9 U3 S/ I8 A! L
        }, nil5 m( m9 o4 d$ G! U
}! M! t8 n/ n8 w3 O7 z7 U
3.找到可花费的utxo
9 t3 U5 O' X  S0 I4 c找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)
  }+ n+ j1 n5 Y" q- v6 N, q: |: a% k// UTXO describes an individual account utxo.
8 h. P' e  f  J& W* ^type UTXO struct {
+ [  A# C/ @( w% a& |4 {0 @7 c        OutputID bc.Hash% I% _$ ^! b& N5 O+ B2 M' N
        SourceID bc.Hash
6 g& Q4 [6 |6 [" B  A/ w4 U        // Avoiding AssetAmount here so that new(utxo) doesn't produce an& E* j& S" p5 d- i
        // AssetAmount with a nil AssetId.
4 J+ O4 h0 F: _, j4 n        AssetID bc.AssetID
# H; S! }5 F+ f* m  y+ @        Amount  uint64
' x: i8 F- @* i3 L& R. g* [        SourcePos      uint64: {! h2 v( t5 q' X3 W
        ControlProgram []byte( R' E+ T5 l. C/ ~/ F5 d
        AccountID           string
( D( R$ _# J" V) Q4 k5 H7 K        Address             string
" e4 h" r) q1 m0 p        ControlProgramIndex uint647 l  C3 ~* M/ E) x0 s5 x5 W' t
        ValidHeight         uint64
& l' Q. H$ f5 L2 V        Change              bool4 ?* E# o" p: s1 @7 ?, H* [
}
% r2 U" o- i' _: E涉及utxo构造交易的相关字段说明如下:$ n# R! T# e, b  d5 S
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
4 X; i6 A& E+ F2 I$ b" _" ?- d5 {- @" F
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)
5 g1 k0 N5 N& M5 Y' A1 a7 k/ [* c// BlockTx is the tx struct for getBlock func
% H+ q6 c. e, H2 g8 v$ i) Z2 Q- Dtype BlockTx struct {4 p! P8 v4 z& `% ?7 ?" k+ z1 [
        ID         bc.Hash                  `json:"id"`7 i7 ~: g9 O' O8 Z8 w2 o8 o
        Version    uint64                   `json:"version"`& C% g9 s0 Y4 \' H* j+ t5 K( q1 y' q
        Size       uint64                   `json:"size"`
+ Q: d" w; X- v' K2 w8 _        TimeRange  uint64                   `json:"time_range"`. ^1 Z% J4 f; ?* [( F
        Inputs     []*query.AnnotatedInput  `json:"inputs"`
. W  Z( V( o. s, H6 b/ H& P        Outputs    []*query.AnnotatedOutput `json:"outputs"`
# B. R  v9 {: z' ?2 T        StatusFail bool                     `json:"status_fail"`3 k  o. x* N# ]
        MuxID      bc.Hash                  `json:"mux_id"`
' j( g& m$ C: p) u: N}
' `0 D6 Z9 t3 j! A$ I//AnnotatedOutput means an annotated transaction output.
7 O+ v3 ]* e& y0 ?+ Utype AnnotatedOutput struct {( `5 _" j& r8 \2 B( Y
        Type            string             `json:"type"`+ {, P  c5 V! E: g, e
        OutputID        bc.Hash            `json:"id"`7 V  h- }$ L: j0 U( e5 G5 Y( ?
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
, \- R$ Y+ M7 j0 u+ `8 |9 N        Position        int                `json:"position"`
) f  F6 z* q: d: \        AssetID         bc.AssetID         `json:"asset_id"`- \# y. }0 c4 v8 F/ X' f
        AssetAlias      string             `json:"asset_alias,omitempty"`
4 u0 i- i. W0 ^: L3 ]5 O        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
% @" u. ?6 V% l% U  Z) h        Amount          uint64             `json:"amount"`
8 P$ y5 L6 d% q        AccountID       string             `json:"account_id,omitempty"`/ V) ~' Y% I; o( n# m  P
        AccountAlias    string             `json:"account_alias,omitempty"`
6 f: C" \! Z" I) }- ^! e        ControlProgram  chainjson.HexBytes `json:"control_program"`
- p* }. S* W# r+ z        Address         string             `json:"address,omitempty"`
) ^1 G) b4 X4 t: }0 ^' K) W8 u4 T}' t3 y: J( P; ?3 D- S
utxo跟get-block返回结果的字段对应关系如下:. _# U; c( L6 s# o. q+ Y6 J
`SourceID`       - `json:"mux_id"`
2 t" Y6 _! s9 X: @5 f) P) j`AssetID`        - `json:"asset_id"`7 J) C2 O( W$ a+ j- }' S% X1 y
`Amount`         - `json:"amount"`
( m/ w3 M# C/ H`SourcePos`      - `json:"position"`. d5 L6 v! q- j/ k* `
`ControlProgram` - `json:"control_program"`/ \: ]3 x" H# l( C; B/ d
`Address`        - `json:"address,omitempty"`0 j" d1 d6 \# o$ r8 x. |6 z
4.通过utxo构造交易2 `- H9 Q" k5 n" a$ V* w9 I( Q
通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。
% q. l2 w. D3 u0 O$ u" `第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:
5 A7 r, i  U( S: B1 W' Z// UtxoToInputs convert an utxo to the txinput; ~* g7 [+ |3 _+ P! m5 u" u7 n, G; {
func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {2 _; n; e* }6 L1 Q4 I+ u2 A- F9 g
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)9 `, C/ g0 |- o4 I, {
        sigInst := &txbuilder.SigningInstruction{}. R/ H8 h, f, P: n0 ~8 c1 M8 R
        if u.Address == "" {
6 x: c/ T# J# I- S* U% j                return txInput, sigInst, nil: x" [  v6 x5 c- t1 k
        }; n0 h/ s- x, h7 E
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)7 S3 a) ^% v9 O, B5 F, \" t
        if err != nil {
: \9 }$ f5 H6 W                return nil, nil, err  r8 Y) e7 i' x  D  D" `; B! ]
        }3 d7 H2 V% {# S! L
        switch address.(type) {( H' G$ I2 O! f6 \& A0 U
        case *common.AddressWitnessPubKeyHash:5 J7 e" Q0 ^5 D
                derivedPK := xpubs[0].PublicKey()# o0 L9 m( }# ^; R/ f1 K  e
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
& H& _2 H' ]$ j& f8 e1 B        case *common.AddressWitnessScriptHash:
, o6 y) E$ ]6 u$ T                derivedPKs := chainkd.XPubKeys(xpubs)1 m' J! ?! X& f( M& [+ s2 C/ g: e
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
# F8 k1 N$ C) i& S                if err != nil {; P( w; a. n9 ~9 x
                        return nil, nil, err9 u- g) O8 j* X. k! O) \5 g
                }  T" }/ n1 m0 _9 z
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))2 o2 K8 w( g' r: v. l( @/ z2 D8 ?5 r
        default:
( ~+ X. V, o2 q- X                return nil, nil, errors.New("unsupport address type")) H) s3 Q7 [$ w, H4 \: s" L. E3 n7 I
        }
- C5 M) w5 R( V        return txInput, sigInst, nil
' K' W# i& U: i/ G* Y: ?0 Q5 y% h}$ {$ }7 z1 M& H) r8 B  G
第二步,通过utxo构造交易输出TxOutput, F6 d) S! W# ~) b8 f) J$ ^/ X, U
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
/ f3 E2 @* G5 T+ r# U+ X: j3 t// NewTxOutput create a new output struct
5 w( L$ w  w/ y! ]9 ofunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
; Y1 r! i) Z: [2 E+ t8 }9 l& Y- u        return &TxOutput{
9 }0 C  E$ X9 M& y, Y9 P                AssetVersion: 1,7 [  D. L& Q0 T3 l6 e5 }8 w
                OutputCommitment: OutputCommitment{
% Q, m  c+ Z  L. V- n- {6 n/ |                        AssetAmount: bc.AssetAmount{
2 d2 W$ Q' i; _. a                                AssetId: &assetID,6 `6 }% z2 G# u2 g. }  R1 L7 ]
                                Amount:  amount,
/ `9 h: }: c: m7 _                        },' g$ x' m' n" h7 a
                        VMVersion:      1,1 Q, h7 _7 d' k& F/ N4 @" w) I9 v
                        ControlProgram: controlProgram,
/ e" @# D5 a# g! r                },1 I+ z, K( d/ y
        }
5 i2 p$ j' F' d, l4 i. T}
- A6 P6 Y! n2 k4 t$ t+ c$ z4 c5.组合交易的input和output构成交易模板
, r4 M2 m8 ^2 b, Z& G' Y通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:8 g7 F  z! ^/ ]8 x) Y# c1 Q) B
type InputAndSigInst struct {
% M) [, l1 a, X( S  e( q        input *types.TxInput
  I4 I' m% c2 z/ p9 t3 |, S+ ^* `        sigInst *SigningInstruction% d) F  T( G4 `( x+ |
}
) G( B$ p( g9 t1 j& e1 A// Build build transactions with template
: u0 X4 R" L$ H0 a. ?4 tfunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
) v" ^6 j- ^" g6 w6 n& d' J6 `        tpl := &Template{}
* l+ e8 m, W1 ]" Q9 S; g: L0 q! ]5 J        tx := &types.TxData{}; D9 D8 e9 U4 G. K
        // Add all the built outputs.
; j  _( g6 M% K, T* E        tx.Outputs = append(tx.Outputs, outputs...)( A7 w7 M, z1 y5 i# K6 }
        // Add all the built inputs and their corresponding signing instructions.4 m- S, y! `* O8 E# T3 ?
        for _, in := range inputs {% m% u" Y% T8 W5 ?1 C+ _9 R) j
                // Empty signature arrays should be serialized as empty arrays, not null.
+ }! C% \0 U& X, i  v& Y, \                in.sigInst.Position = uint32(len(inputs))6 s* n/ M" i, u4 O
                if in.sigInst.WitnessComponents == nil {
4 e( s6 j! o0 ]) a+ J; P+ C0 a                        in.sigInst.WitnessComponents = []witnessComponent{}0 h: ?  Q; m3 A7 L8 z- a( ^$ C4 Y
                }$ y0 l/ i5 q. r+ n2 ^
                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)5 w6 h0 [# r, V: r" P! {/ {1 ]) J$ R
                tx.Inputs = append(tx.Inputs, in.input)
3 |+ {. C8 N9 _3 W        }
$ `' w6 h; y9 ~        tpl.Transaction = types.NewTx(*tx)
' w/ W' O, M: d- X  K2 {        return tpl, tx, nil
# ^0 z) C; r/ M1 S8 X& Z0 ]6 S}
& a3 g3 t5 C' R6.对构造的交易进行签名
1 U- _/ _( Q& {. D1 m  i- W* }账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)* f3 i# n8 H: W+ }# ^+ p
// Sign will try to sign all the witness  h6 {5 b  k$ Q$ `* B3 b3 Z
func Sign(tpl *Template, xprv chainkd.XPrv) error {' R7 `* [3 R0 n. s: d! |" T
        for i, sigInst := range tpl.SigningInstructions {( t) I- ^. y, t1 X& _
                h := tpl.Hash(uint32(i)).Byte32()
  t3 h2 G/ ~' D+ r                sig := xprv.Sign(h[:])
! l: Q4 i" `) C; X1 m" |                rawTxSig := &RawTxSigWitness{6 F! A8 s$ b! k9 [/ x( A
                        Quorum: 1,
. Y( y% N( }$ G! W$ M% a                        Sigs:   []json.HexBytes{sig},
! U/ ~3 E( H- @1 f! d                }" ^9 @: @7 v0 G5 D6 F( X; t
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)+ z0 F& s: n! `! P
        }
6 |2 b; Y# p2 D6 J+ [. S        return materializeWitnesses(tpl)8 U. v2 A9 g% P+ K- [2 u( V  v, a
}
8 W* v* l6 j$ [6 v9 q& _7.提交交易上链6 J5 o# Q1 i5 n9 f
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可3 B8 B7 G# O* G3 t. o0 g' n

8 i+ O, ]) B6 Q: c' G
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16