Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:8 v: U( `& k* U, d# X
Github地址:https://github.com/Bytom/bytom5 [+ O' d' _( ^0 r- @/ B( ^. y
Gitee地址:https://gitee.com/BytomBlockchain/bytom7 w, W$ K5 D) K: [4 s9 x. @
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。0 n9 {- ?3 u& h1 K
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链4 r/ j- z& k6 R9 N! P7 ?

" p3 J, n" {3 X2 m+ b4 Q注意事项:
, O! C( C+ w3 }; c4 `8 a以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
! x/ g" S; D% k5 a  Y7 Z1.创建私钥和公钥
1 ^2 n/ {# K3 H0 H该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥( l' k  u! G$ M6 L" O1 T; {
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
! S4 {6 V2 U/ @" h. p1 Y3 v        xprv, err = NewXPrv(r)! ?( V: k6 {8 g; `, O0 f; }5 n* `( }; z3 `
        if err != nil {# `& I' i. ]! R% H$ J7 O) M2 E
                return
8 [2 ]! i' [) q4 t; \3 ?9 z. F        }& i) L7 D2 v$ ]. H0 K  @
        return xprv, xprv.XPub(), nil2 }. K' Q' V( p5 d2 M2 H
}% D* I' q  ?- Z1 P/ P+ p
2.根据公钥创建接收对象
) v2 u" P1 d# R/ D* D接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:' M5 M* J2 {5 n+ \, Y& x
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
3 `) f" t( z3 J0 ^$ o. c        pubKey := xpub.PublicKey()
7 C2 x9 k1 X5 [& G( o0 L        pubHash := crypto.Ripemd160(pubKey)
( @+ @6 a) U) i3 _        // TODO: pass different params due to config; y; l+ `3 D' J3 R* b  [0 U9 o" @' e
        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)0 s% D, S8 K4 E" a2 K9 K2 n
        if err != nil {8 j1 x0 A* U/ F+ G+ a
                return nil, err
& b" {0 b% J" q9 ~" W6 L        }
2 k3 c9 Z; c( n4 }        control, err := vmutil.P2WPKHProgram([]byte(pubHash))/ ]- c3 e5 t% B9 |; m+ X
        if err != nil {
, ?) g  `. G! p& K                return nil, err4 {& B  ^+ h4 C& Y+ g- o, G* {
        }% J) a9 }9 s' q: \( h
        return &CtrlProgram{- i# v& d3 C8 p0 i& n9 J( ^
                Address:        address.EncodeAddress(),5 h4 j8 p2 {- Q! K+ |7 S/ j
                ControlProgram: control,  M/ r. c( y' Y! }7 m% n
        }, nil
+ K( _( b, S) V0 q. X! F}
4 T; b  B, z" D5 p; [创建多签地址参考代码account/accounts.go#L294进行相应改造为:' g: a% h$ w' S, ?. u: i" E8 R
func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {
' G' Z- D$ G6 B# j) Q! Y        derivedPKs := chainkd.XPubKeys(xpubs)
' a/ D" S/ }: N        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
9 R0 o( C7 R8 w6 F) x% O        if err != nil {8 k" I- \7 M, V9 b
                return nil, err
6 `, n9 Q8 k' G        }
  k1 N/ V6 p6 p* b% h        scriptHash := crypto.Sha256(signScript)' Q& x6 `9 `# j; O! m$ D
        // TODO: pass different params due to config3 X' C$ K  o: F: ?( P
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams); E: A3 V' t& D" ^
        if err != nil {
( D5 r1 b4 @2 Z                return nil, err5 q% m  h  f0 B8 h' H
        }
/ [! H" U/ o% z9 ?0 v' I        control, err := vmutil.P2WSHProgram(scriptHash)6 w5 r7 I1 t6 }4 A
        if err != nil {3 I/ L4 B( {  i2 q8 b2 B
                return nil, err
. D3 A7 R1 [# g7 R; A9 \" A- D% E        }, c; P  p/ T- K' E9 c: @/ T  `
        return &CtrlProgram{
" I6 B3 |& {, k8 F; E+ f                Address:        address.EncodeAddress(),4 e! Q  S! K( k) [
                ControlProgram: control,
( r6 ^9 s1 u& `; E# L  {+ W2 m3 {        }, nil0 m3 b* P) P# [5 H
}0 T5 I" M  D. I# |( Z/ [
3.找到可花费的utxo
% w4 V: s4 b" K" i( T找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)/ k. w4 d. b5 V0 l' Q/ p* d: b
// UTXO describes an individual account utxo.
. s0 m0 [# {2 ~( Itype UTXO struct {, Y  ?' ~/ H6 p
        OutputID bc.Hash
2 k  B. {( I4 y: c6 g        SourceID bc.Hash
% ~' j7 |+ \1 r/ s- h4 M* s        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
0 {8 s% @$ \  c% f        // AssetAmount with a nil AssetId.
4 N9 X4 H1 d9 j. `* F        AssetID bc.AssetID
; W1 E# L& h+ z7 I" ^5 e( G        Amount  uint64
  f. L2 m0 D' ^8 ?- {' h+ O6 \1 V        SourcePos      uint64% y* r) e- I. C( S- T6 \
        ControlProgram []byte* z6 K: h6 \7 Q0 u2 V9 \6 L9 ~
        AccountID           string
7 v5 |, o: C3 J: x4 y( h( [0 w0 o        Address             string0 D' F4 G- x8 }
        ControlProgramIndex uint64
+ I7 T$ m( W* e( Q. N        ValidHeight         uint64
$ c# d( c" p% C6 e6 v        Change              bool3 U, w6 ~" y/ U) }9 k$ e, S
}
. A3 ~* ?( i% L( H0 p涉及utxo构造交易的相关字段说明如下:
# a! K% |+ x. d0 ]# C- B4 XSourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
4 l! Z8 r; Q& B2 X8 ^6 P- @0 C, k: H! |; u1 s& z* E! @3 f
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)# J5 A+ ]8 I' o' B( ]2 b
// BlockTx is the tx struct for getBlock func
0 Y& U* ~9 K0 w1 r6 M: B. X& Ctype BlockTx struct {
. B% Q. Q! |# M7 e2 [        ID         bc.Hash                  `json:"id"`
3 Z4 B3 c+ k. W, p# f6 m        Version    uint64                   `json:"version"`2 ?0 z+ c# n! C. _/ [' J. c3 N- y/ V
        Size       uint64                   `json:"size"`
4 d9 }7 [% L% s; |. D        TimeRange  uint64                   `json:"time_range"`
( \7 A. G- d) w" [        Inputs     []*query.AnnotatedInput  `json:"inputs"`
+ ~4 h% X+ |; S9 ?% h% i3 U# m        Outputs    []*query.AnnotatedOutput `json:"outputs"`! F; O7 [1 v" V7 [
        StatusFail bool                     `json:"status_fail"`
( J- v7 f9 R; N' P/ l. i' M        MuxID      bc.Hash                  `json:"mux_id"`
* {( k, k. y" e0 u1 H2 H* \, O}4 J% ^0 ?) o: N# R* ]
//AnnotatedOutput means an annotated transaction output.: V4 |3 K- Z# Y1 S. S9 S/ q
type AnnotatedOutput struct {
+ Q; X9 y, M3 l; v& U        Type            string             `json:"type"`/ |* I5 B2 r9 ^4 Z3 B
        OutputID        bc.Hash            `json:"id"`$ O! v# E9 s2 z8 C6 C6 @$ W4 D' v
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
( ]; H4 J: ]' t, u        Position        int                `json:"position"`. t" R+ }5 {; F: `- a
        AssetID         bc.AssetID         `json:"asset_id"`
: u" e( B+ T3 V7 M! ^" \        AssetAlias      string             `json:"asset_alias,omitempty"`/ Q- ^, b; j7 t
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
/ C! U: ^. u# D) p2 t        Amount          uint64             `json:"amount"`9 e( P/ P5 m' U
        AccountID       string             `json:"account_id,omitempty"`% d* d+ S0 r$ l6 E) k/ w
        AccountAlias    string             `json:"account_alias,omitempty"`
- Y/ {& W+ ^1 L" |% P; W        ControlProgram  chainjson.HexBytes `json:"control_program"`# l% K! K1 {. }; U3 y3 |
        Address         string             `json:"address,omitempty"`
: E/ R# f9 V+ Q* g3 g. n. e}
3 N& @4 [0 `3 }# y; ]9 _1 Dutxo跟get-block返回结果的字段对应关系如下:
* B0 t, o& O4 E+ Q& q) J# L; J`SourceID`       - `json:"mux_id"`9 x7 M! G4 K  }+ @
`AssetID`        - `json:"asset_id"`
- ?: x5 c! j5 \) i`Amount`         - `json:"amount"`9 _! v* `4 K& l% k1 E* ^
`SourcePos`      - `json:"position"`; ^( F- i# f5 H! v4 x) E2 |* w
`ControlProgram` - `json:"control_program"`
* f: |8 P3 _& d" Y2 e6 l- ?`Address`        - `json:"address,omitempty"`
" }* w" j& z4 V6 }8 w$ Y4.通过utxo构造交易
+ x% p1 J0 r0 O1 t, J7 r& s! p通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。
4 B* I& U: g; M. O2 ?4 I第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:. ~7 D1 g$ I  a& @! v5 u3 ^
// UtxoToInputs convert an utxo to the txinput
4 x. V+ x& C3 q3 P; Vfunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
3 X/ f, K1 z  R! Q* ^( |        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
/ `4 \; a9 g+ l: }# d        sigInst := &txbuilder.SigningInstruction{}
8 b- M4 Q5 }5 Z' K/ x- v5 _: @        if u.Address == "" {+ u/ {$ m/ t. G& T9 x# M' M
                return txInput, sigInst, nil8 }! O0 @" s: q- [2 U
        }
. E" z0 n7 P& i, V' n# `# [" w+ `        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
2 O# v( ~7 @1 n/ V* [' |        if err != nil {% a8 f2 n- g+ @5 y% O5 k5 X
                return nil, nil, err
% |. q3 n! ], W. Z0 F  h* X        }
& c+ t. C) b( |        switch address.(type) {
0 \# N6 y" P" d! i# H. ^        case *common.AddressWitnessPubKeyHash:. B2 d! l9 V$ x9 y; w. P; }8 y
                derivedPK := xpubs[0].PublicKey(). T9 L# z, T  Q4 n
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))1 ]' ^3 t+ G. s- e/ Z' b4 g
        case *common.AddressWitnessScriptHash:1 P5 X8 `: A  m. O6 V
                derivedPKs := chainkd.XPubKeys(xpubs)
$ h# R$ p* k; J5 U4 y                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
# Z2 q8 z. n- L$ x; c0 A                if err != nil {+ f" p  S6 i4 |$ `% c0 ?
                        return nil, nil, err
+ y  k  o# C  W- ~4 v3 k& v& l                }/ Y8 q: {: M: p% U
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
+ B# p- O4 s) z# \        default:" c6 \. b) y$ V) s: l+ x$ E
                return nil, nil, errors.New("unsupport address type")8 _; j1 b, y) g
        }( H3 x' `# c$ q  i# S9 g
        return txInput, sigInst, nil
: S, E8 V; ^% h}% C5 ]9 p- Q& ]' G
第二步,通过utxo构造交易输出TxOutput; Y0 ^" C. q* D
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
( r5 k% G, H) u# S1 e0 b+ \// NewTxOutput create a new output struct
- e6 s5 v0 E3 u. X3 `func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
5 ~) u9 E1 m! w& g! X        return &TxOutput{! }* g3 |' c: \1 N8 D
                AssetVersion: 1,1 M2 b  C7 }/ {2 P1 _
                OutputCommitment: OutputCommitment{
) H2 z4 x& ~1 y/ U; n1 e+ C$ k                        AssetAmount: bc.AssetAmount{
; V2 \+ V& R- D" V; R                                AssetId: &assetID,) R& U/ @) j" A- K6 z$ R
                                Amount:  amount,
* ]: e% e/ q' s                        },7 Y# {+ s) X1 c  |9 B
                        VMVersion:      1,
$ c' o2 A9 t9 E, k                        ControlProgram: controlProgram,
. d: Y! d; x( a! {  E$ ~                },0 W9 T0 W* T" `" ]2 I$ K
        }
% ]/ a- M, d1 H+ z}/ f. W8 [$ H8 d: S# f) H* E
5.组合交易的input和output构成交易模板
/ C  W& l0 [# @6 Y6 f) T通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:$ b! ~+ D/ O" U- O$ O" ?* E
type InputAndSigInst struct {
* j# Q1 a5 q& c( }! x1 T$ @        input *types.TxInput
5 |6 m- V7 R6 s0 i0 G        sigInst *SigningInstruction5 q8 N1 e; }2 @) s9 t9 X, L
}1 m6 Q2 @0 V# K, m* R0 ~
// Build build transactions with template
* R. F# I! j" t' Afunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
' {% m( |' V/ ]! u* h        tpl := &Template{}2 ?0 J, }$ x. Z, v
        tx := &types.TxData{}4 n" R3 E3 }+ Y
        // Add all the built outputs./ G1 K3 v4 c$ y5 Z$ O0 O/ T
        tx.Outputs = append(tx.Outputs, outputs...)/ {/ X: W* o3 g
        // Add all the built inputs and their corresponding signing instructions.
' p8 H0 v# M- n- l4 F- [% d        for _, in := range inputs {
+ T2 ^6 B; k4 R                // Empty signature arrays should be serialized as empty arrays, not null.& M. z+ S& H9 Z, i6 s- u3 U/ }
                in.sigInst.Position = uint32(len(inputs))
& p6 }* E6 I( v5 z: N  `4 ?. Y                if in.sigInst.WitnessComponents == nil {
0 F9 g3 T8 T' j. t/ t                        in.sigInst.WitnessComponents = []witnessComponent{}
( {; o- U3 b1 R! D2 O* s                }
! ]) X; w  ~5 }: E- l5 n                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
) o+ ^  ^; y" f9 ]9 x                tx.Inputs = append(tx.Inputs, in.input)
7 M6 P  ]+ Z' h+ S3 Q/ J( [        }# f/ E, A) Z5 l" V0 Y; }' t0 \
        tpl.Transaction = types.NewTx(*tx)6 T! a& n8 k3 }" u1 d& n
        return tpl, tx, nil
+ E  e& U6 C% x+ V3 U}
( F* G0 X: m  `4 G6.对构造的交易进行签名
8 s. S8 F: q$ {" C- r$ ~( j账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)1 H- r# u( t# u
// Sign will try to sign all the witness' Z% H( v' O: y  A9 |* K+ ]+ p3 V
func Sign(tpl *Template, xprv chainkd.XPrv) error {
3 r' a: Z2 R' n. ?. D# t        for i, sigInst := range tpl.SigningInstructions {3 B. [: C% M1 s2 x6 v
                h := tpl.Hash(uint32(i)).Byte32()2 \: S" N) [7 `9 T& A* b
                sig := xprv.Sign(h[:])
! Q9 f8 I5 H, D2 v9 P                rawTxSig := &RawTxSigWitness{( i* e5 @9 c5 Q1 B6 n+ }
                        Quorum: 1,
) ~; [" L1 c& {                        Sigs:   []json.HexBytes{sig},
; N; B2 x1 n; _                }
5 W% h$ }( A% P0 t" Y) g                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)7 ]& V6 G3 S. P& ?& h' q
        }
% ~: w4 W. o' z4 k/ k2 W3 P3 L        return materializeWitnesses(tpl)6 V, o7 J5 q: |8 t
}* }/ R' Y. _+ R$ x
7.提交交易上链7 w. ?0 D' V1 c5 _; T
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可1 H) I' E) [; |& {
- C) d2 }; y* B) x: `4 s
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16