Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
  {/ T' m: P: a+ I7 }3 T9 }. N$ YGithub地址:https://github.com/Bytom/bytom
& k6 G* C8 y' y. e# W0 fGitee地址:https://gitee.com/BytomBlockchain/bytom
- {9 W2 |8 f2 D$ `" R& x9 h! u该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。. c3 K, M9 D' f% b) g0 }) ^
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链  x5 Z4 @: n3 W! F$ G$ N

0 ]* I7 V" o5 L; _5 h( Y$ [. O+ x注意事项:
& H# R# G( n* w, d. |以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
" f% U* J- W; v8 q4 r$ X1.创建私钥和公钥. b' M0 `0 h: R4 h$ {
该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥
8 B/ [( z) i9 Q: ^! Y( nfunc NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {) N7 Q* E# m3 ~6 U1 Q% y) z
        xprv, err = NewXPrv(r)
' `1 \7 R) k: n" h        if err != nil {
$ ]5 a  y3 a0 B! K6 D                return
3 I4 V: `" k# e: `7 d# |8 T1 B        }
# L3 o2 Y. R$ y! ]' w. ^$ r4 |        return xprv, xprv.XPub(), nil% M* ~* O+ c7 m( \0 I
}# t" k) b+ |8 f
2.根据公钥创建接收对象
" A" M" n* f$ A, v4 M5 G4 [接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
% y, O# ?- j% X- dfunc (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {' z2 t8 O8 q3 v6 W( s7 u
        pubKey := xpub.PublicKey()
# Q0 z! z. Z7 w/ S) f        pubHash := crypto.Ripemd160(pubKey)
: v4 p0 M4 C" P# w* P# ~        // TODO: pass different params due to config
. W/ x. Z! N) Q- O. y* v" `        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)+ x& p+ o. B( ]/ R9 j* f
        if err != nil {* c1 I+ V; `0 V9 O% s
                return nil, err
2 V3 E% u( ~  @# Y+ _" c        }
4 z! n  h/ {2 n7 h$ f5 n        control, err := vmutil.P2WPKHProgram([]byte(pubHash))+ K2 d# k8 y+ h
        if err != nil {
6 N& T; T  j8 F$ P% c                return nil, err
' Z1 z7 L9 ^3 s( e$ t9 M        }8 \  Z  L' G2 n  o
        return &CtrlProgram{
) d; V* z# I! x; `                Address:        address.EncodeAddress(),6 |- O9 p0 D4 \! q) m3 @% f. G9 t/ X
                ControlProgram: control,3 D, s/ Y4 A4 |* ]1 |
        }, nil
0 s' A2 n% f8 k$ X/ ^$ `+ Y0 q4 z" L}
3 S6 k5 G- g; \创建多签地址参考代码account/accounts.go#L294进行相应改造为:2 ]' P$ q+ i4 U. b( w. z+ h
func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {" _5 Q. N4 Y0 ^5 t2 W1 a
        derivedPKs := chainkd.XPubKeys(xpubs)
  b& Z  l7 B. c1 ^& H        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))% A: V, r9 V* q0 f+ f( Y+ L# z" I3 n
        if err != nil {: T) V/ Q  C8 E! @
                return nil, err0 ~& g, a! d3 h9 v& p
        }
. n6 Y/ [; G6 R) _" f        scriptHash := crypto.Sha256(signScript)
2 Z% J$ G& W8 m, X: P7 ?  J        // TODO: pass different params due to config
9 O, z! b3 x& H0 l        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
% @/ T+ z5 n# m* K1 ?' D        if err != nil {* r( [; }! X, L5 e, }0 ]
                return nil, err& h8 V- B+ R2 ]& v; L8 l7 ?
        }
! I: \( z% u0 }) k        control, err := vmutil.P2WSHProgram(scriptHash)2 {" @6 I( `( O
        if err != nil {
/ C7 d8 A8 W& m/ N& x' j                return nil, err1 b1 g  X: |1 Z/ m% E7 `. P  Q
        }2 X6 L' f: L, ]. c; ]
        return &CtrlProgram{$ n1 b2 k3 U/ X! }5 Y; _4 {- C
                Address:        address.EncodeAddress(),
' A6 e! S& P! `3 {                ControlProgram: control,7 F$ F" l4 f: e. G( v$ J) P
        }, nil, t: l5 m8 z" l. {( T+ ^8 R. W
}
/ \0 ^) _* Y. b7 y$ A3.找到可花费的utxo
- c& [% n  T* E0 n* c0 T找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)5 C5 D7 N4 N! \7 i7 ^) [3 r; G+ J$ O
// UTXO describes an individual account utxo.1 w/ h* A& d  O( X% A$ X
type UTXO struct {8 c( j  x6 }, o
        OutputID bc.Hash
# U2 S% M+ i, f. n3 y        SourceID bc.Hash7 _6 m9 C4 z" e, g7 l
        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
& w0 G+ e: S  I8 S, W3 ]7 b        // AssetAmount with a nil AssetId.
" X# g* ?, i6 P( w- X( h9 |        AssetID bc.AssetID/ z9 W: D( ~. \4 G; y
        Amount  uint64- Q5 {) W( }! a; B9 y
        SourcePos      uint64
% X) I! ?$ C. m        ControlProgram []byte& ~& B7 ~. D3 x: \" I3 V: T
        AccountID           string
. r: j; G4 r- ^; D, |/ _4 m) m4 z        Address             string
8 W, I- V- k! c. `( n        ControlProgramIndex uint64
" d: `" u) \! o, [) r        ValidHeight         uint64
, B& G6 W6 X; p. k; n3 X        Change              bool
8 d0 ^5 R5 a, e; _/ e+ w* z}* p# Q, p9 m! d* I2 Y9 L/ z  f
涉及utxo构造交易的相关字段说明如下:
- S+ \, k  Q2 T* y# HSourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址; @' O8 ~- k1 n# f+ ^& E
2 k9 t) J+ R% ~
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)4 I! S% a6 B) t9 m5 B  a1 C
// BlockTx is the tx struct for getBlock func
; _. u0 A& w) [- Jtype BlockTx struct {' ~! e  _  v3 o! `2 g# o
        ID         bc.Hash                  `json:"id"`9 l2 ~, p  [. a
        Version    uint64                   `json:"version"`9 I+ C1 i! U' u( |; P0 e
        Size       uint64                   `json:"size"`
$ e* M3 x) t, [9 t- H6 G" H  H+ V$ r        TimeRange  uint64                   `json:"time_range"`
' e5 S( |& i. Y4 ?        Inputs     []*query.AnnotatedInput  `json:"inputs"`
% B$ G& E( I) g% N. Q( V        Outputs    []*query.AnnotatedOutput `json:"outputs"`9 Y! X. l# W- `3 m
        StatusFail bool                     `json:"status_fail"`
/ }+ z( W( A2 R5 J! J( `        MuxID      bc.Hash                  `json:"mux_id"`
& S& y9 |% s( {3 F5 s8 K8 i% [* Q}
$ N7 a6 L3 h% @" E- M7 u9 U//AnnotatedOutput means an annotated transaction output.# n! J+ @/ l# d0 o" U+ K4 W
type AnnotatedOutput struct {
& `( O- I$ j; ?9 M2 Y        Type            string             `json:"type"`# o5 M. }; k0 i; }$ O
        OutputID        bc.Hash            `json:"id"`: C+ {& a0 E* e( p2 E- V$ |
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
( E% [1 ~: Z- @* [: f0 I        Position        int                `json:"position"`
" j8 B) N$ r( K8 _' \! r3 ^( ?! c        AssetID         bc.AssetID         `json:"asset_id"`; P6 a' U1 ^5 M4 `5 s: O# S
        AssetAlias      string             `json:"asset_alias,omitempty"`
. F# [& P7 t* m$ w% _        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`% l- n# [" O% m( }1 B/ C" h" C
        Amount          uint64             `json:"amount"`' y# O# B3 S8 V' \! F: Y+ P
        AccountID       string             `json:"account_id,omitempty"`
& e, t6 u  V3 [( o        AccountAlias    string             `json:"account_alias,omitempty"`
7 Z! N5 S1 V0 m3 a" r        ControlProgram  chainjson.HexBytes `json:"control_program"`2 j' s- |+ r5 \# W& Y, W; e
        Address         string             `json:"address,omitempty"`" i* Z3 _* s+ p9 I
}: g8 |! f8 z. k; {1 ?
utxo跟get-block返回结果的字段对应关系如下:
' }5 g# M) ~% M8 a  c& m`SourceID`       - `json:"mux_id"`- k- M; q5 Q. h. L! {) H
`AssetID`        - `json:"asset_id"`
6 S/ w* ^0 _: g& b4 R- Z`Amount`         - `json:"amount"`
; u# d" {5 E" u/ O7 B+ x`SourcePos`      - `json:"position"`
& ~; y8 e  G& ]2 x" v`ControlProgram` - `json:"control_program"`, n( Y$ A! t0 ^+ @4 o/ Y( y" c
`Address`        - `json:"address,omitempty"`
: n9 T3 X" @0 }9 V6 ?4.通过utxo构造交易
6 V4 u" F4 B! T7 m6 a通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。/ q, U2 c) \+ i+ E) d
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:# |  w5 z4 c& X, {3 c% y. m# L2 A
// UtxoToInputs convert an utxo to the txinput+ [# o; C1 a0 E: ]( t/ d. ?, K
func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
8 Y! A- S9 m+ C% s6 c" |& X        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram): s4 x4 f; e) E! D" B4 |0 u" {
        sigInst := &txbuilder.SigningInstruction{}1 e  {$ {5 t3 u6 G+ ?, B; c' j
        if u.Address == "" {
3 C; }- _6 q, m& R/ C                return txInput, sigInst, nil* \/ D# I# s. {
        }9 B7 f3 v# o; N) P
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)1 x- V) h' ?$ N" \' {% m  S
        if err != nil {1 I; v; b& N: d  X9 O- Z& a/ v
                return nil, nil, err
+ W$ @# ^5 f; ]! \) a0 {        }7 v0 F, e! L" k/ v5 m4 o
        switch address.(type) {
: |' F4 ^4 q9 @" j) T        case *common.AddressWitnessPubKeyHash:
2 J2 ^9 X& O! M* Q9 E. y6 N+ x: o                derivedPK := xpubs[0].PublicKey()
' m5 V8 p; G* p) k8 ]                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))& B; C  |' g6 L- q6 ]5 \+ Y/ m
        case *common.AddressWitnessScriptHash:3 {# v1 }/ }7 G) ^7 x
                derivedPKs := chainkd.XPubKeys(xpubs)$ q7 M; y+ I& ?5 |
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
' M$ T5 x6 E3 X3 H: {8 D2 F) H                if err != nil {  A/ X* L  g" s- @' Y7 X7 [( l, \6 ~
                        return nil, nil, err
, A7 H# l, d, b; U  w# }- K                }
4 j5 c) c: V3 h  d- S                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))2 X1 m7 j0 T6 |& j1 L9 z3 @1 [
        default:
' Y* p7 n) z9 i7 j6 P) _- c9 K                return nil, nil, errors.New("unsupport address type")5 \; S) y8 ]! y( ], f( @2 [
        }7 x' K/ a8 Z! w6 K
        return txInput, sigInst, nil
% Z# w: e$ ]4 P. w6 K1 z}" J0 j3 [' M* R
第二步,通过utxo构造交易输出TxOutput
" X9 f' M5 U/ B, \8 _该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
2 E9 @1 E" Y& Y% R, f$ a// NewTxOutput create a new output struct
% F, v& N0 R4 q$ D/ c4 Sfunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {+ _( a. r. ?  v
        return &TxOutput{
  a4 B6 N" p5 [9 G. `0 @( U1 m                AssetVersion: 1,  M& X& z. S0 G' ?. _9 q! k. \! I
                OutputCommitment: OutputCommitment{, _* k4 g7 {' [3 I: z/ i& F6 u
                        AssetAmount: bc.AssetAmount{
, ?6 I) A8 y2 q' I8 s, H/ ]                                AssetId: &assetID,
8 Q) Y# _1 @! X- Y! o1 n                                Amount:  amount,
5 v8 c6 U4 f/ O/ n                        },7 Y" f" m" S/ L$ m
                        VMVersion:      1,
# u5 e" i9 X3 Y2 ^& n2 R* ?3 o                        ControlProgram: controlProgram,( A9 @3 i" K. O. r0 {/ [
                },4 G" Q8 b; l, y
        }9 {# S" x' s- d
}
( a  _$ V" J( ]* g+ e" H1 ?5.组合交易的input和output构成交易模板
/ o7 r& z9 A. W3 }* L# o! G' ]通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
: h1 K+ F5 w) p* g2 I, a2 D6 Wtype InputAndSigInst struct {
6 M' w/ _  M2 s! n        input *types.TxInput
3 }* x3 b& D1 |2 a$ |- F* P        sigInst *SigningInstruction
& A$ k( |8 }( ^# `) ^, t4 z}1 A$ t* V' }! B, p
// Build build transactions with template
- J, ^+ U1 r( Q$ X7 K$ e5 Sfunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {- W( A9 C# A& {1 O9 v1 F9 d
        tpl := &Template{}
2 J0 V0 p5 A. h* g  J        tx := &types.TxData{}
$ K) m" A. `0 T- r: y& C        // Add all the built outputs.* F9 F& x+ p7 _2 S
        tx.Outputs = append(tx.Outputs, outputs...)
) I$ Q  b* m! b        // Add all the built inputs and their corresponding signing instructions.
3 V  a0 r/ g- j2 B+ M        for _, in := range inputs {3 A" N; K* G* ^) O) ?* C, R
                // Empty signature arrays should be serialized as empty arrays, not null.: g- ^$ f! Y9 L# a
                in.sigInst.Position = uint32(len(inputs))
. ?8 s" q+ b. h; E! p0 w9 ~  g                if in.sigInst.WitnessComponents == nil {
$ I- g0 b% x" E( n# ]) t) f  k# `0 k4 U                        in.sigInst.WitnessComponents = []witnessComponent{}
, A0 M/ [- ~- X                }+ `* J; S( K9 j+ N3 E
                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
1 ^  b% O7 {" D                tx.Inputs = append(tx.Inputs, in.input)8 ^. ~  s2 K( h7 t: @1 j. }
        }1 d! |! n( R3 ~3 C2 I. {/ F8 z
        tpl.Transaction = types.NewTx(*tx)( k. c( l  u: Y8 o- i' Q% F
        return tpl, tx, nil
1 F! Y: x2 l; q! C7 J' T+ h9 a}0 x" ]4 {" {5 s# L# N# J' c
6.对构造的交易进行签名
( d/ [( {8 T$ q  S$ K! j2 c) I! T+ h( h账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)
$ k, y6 H6 I4 S* u$ D5 K" D// Sign will try to sign all the witness) A" \3 ]$ Q- @$ J- w! j
func Sign(tpl *Template, xprv chainkd.XPrv) error {; g6 f9 m; G+ Y* F( E- x
        for i, sigInst := range tpl.SigningInstructions {
- p5 F9 `$ i0 Y/ {                h := tpl.Hash(uint32(i)).Byte32()1 D. I  \3 ?: U2 r; h: x2 x) o7 X
                sig := xprv.Sign(h[:])
7 B; G* {, t& D5 c: V. r                rawTxSig := &RawTxSigWitness{- }# W4 F" \' ^
                        Quorum: 1,
' T3 H. ^2 N( A. v) H) D8 ?5 ]+ ?                        Sigs:   []json.HexBytes{sig},
( N, F% u& \! i/ R; y, @# Z                }
' s3 s, t7 F# V: ^                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
+ y% \/ A, Y  x  {5 e8 D        }* [/ m& n( o$ h4 z: a- {  l
        return materializeWitnesses(tpl)! Q8 l* w, W: Q( q8 @/ U) Y3 b. G
}+ W8 _( ]* {6 a, S6 p
7.提交交易上链8 V5 T0 [" I, A7 e7 m* J
该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可0 U+ I  k+ P; M0 o
/ ?$ H. C# D9 j0 j& i: _! O; n# H7 i
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16