Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
" n7 d& V' n( n0 z7 _) t; B( wGithub地址:https://github.com/Bytom/bytom
, z+ Z0 U9 h5 _. [0 z( N$ x  fGitee地址:https://gitee.com/BytomBlockchain/bytom
( x8 b. N9 R: G7 n% F该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。: k- J  N4 M! {& n# ?7 |
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链5 k- V6 P9 r# |
! Z. w4 O  @% L+ B% w
注意事项:2 G. _% _. s8 E% x% ^) Q
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
$ x, T  g& ~' [+ |1.创建私钥和公钥
3 X9 @0 z# I' ?- B3 @该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥, J) o, h$ L* Q  H! }$ U0 w
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {9 n# N/ U7 i  q# f! u4 H
        xprv, err = NewXPrv(r)
4 o" P6 z4 _5 L; z$ a2 q5 P        if err != nil {7 N0 j. R  M- J- }7 b
                return
0 J' {6 e6 I0 k# \2 c$ c        }; h6 }1 M6 u8 K# o; `
        return xprv, xprv.XPub(), nil
& Y; o7 z* @2 X0 Z8 ]}  x2 q1 x7 J, Z# K" h
2.根据公钥创建接收对象
( M; o& g: s  I- r9 y( |1 v接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
' f8 N0 w  B3 n5 {: Dfunc (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
* U  Z( U8 C2 Z6 b. ^" p2 O        pubKey := xpub.PublicKey()
; P5 ^9 j# M7 n# h        pubHash := crypto.Ripemd160(pubKey)
: a. [+ p, G6 `; q' R        // TODO: pass different params due to config+ {# V/ a- n4 \6 s
        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
$ Z- F- T& t5 E        if err != nil {
' X  {4 v1 m. h3 m8 ?1 Z' T9 e6 B                return nil, err; K. l. P/ e$ l. _# ~
        }" V! J5 l8 r+ z/ Q
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
/ P+ E8 u/ q. R; [        if err != nil {( `3 k8 A+ l. ^' ^  P" r8 z* X
                return nil, err
1 N$ y8 s" j7 i- g7 O& d        }/ o* o8 h- P2 K  F
        return &CtrlProgram{* K& Z4 q/ D! S4 I
                Address:        address.EncodeAddress(),
6 q2 T4 q3 |2 `$ v1 Q3 ^                ControlProgram: control,* Z# U/ [. j( W( U6 i* Y
        }, nil
* y+ Y/ [2 u: K% }* ]6 N3 m" U}
' q2 C  w, g$ D' ~" N4 p" H1 C* [% ?创建多签地址参考代码account/accounts.go#L294进行相应改造为:
) v" K& P9 R& d- H4 `func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {4 f1 l! c' }1 S! u' o
        derivedPKs := chainkd.XPubKeys(xpubs)$ H/ N; J, n: T% h  O( O; n
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
  q* H$ g* l. p0 f        if err != nil {7 A1 M, I8 Q. j( v* ^0 \, s: H
                return nil, err
) z* \$ i- F& \! k' y6 x# m! A7 K: S0 s        }
  y, p+ x1 u$ @4 r        scriptHash := crypto.Sha256(signScript)6 ^) u% E2 s( G  L1 o# s
        // TODO: pass different params due to config
5 z2 r5 k, b! s( M        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams), s2 D7 q7 V% ?
        if err != nil {
9 z% C- s( D( T1 Z6 x                return nil, err
  F4 |) K1 [/ q3 M2 \8 Z        }
+ ~5 G9 s2 c0 l* R) ?/ C        control, err := vmutil.P2WSHProgram(scriptHash)
) G. Q9 {* ?+ L4 C& k* K, e        if err != nil {6 n, B' {* A- l+ D
                return nil, err
$ u( r  a7 P8 x$ t        }
' w1 j4 t, v, Z, S6 T# y& H  Y        return &CtrlProgram{' v) F- ?" k, f6 A
                Address:        address.EncodeAddress(),
3 i# M: a$ f% [  {                ControlProgram: control,
5 F8 @9 U3 m* e        }, nil
8 g; _7 J  Q  e& D  j}
  Z- _/ N4 }1 v2 O1 g# ^" M3.找到可花费的utxo
, E. j8 Z+ J4 m* ]( S/ ?2 t找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)4 a$ j5 l. l& v5 X
// UTXO describes an individual account utxo.. s4 Z0 X7 l5 j1 K; V
type UTXO struct {
( ?& b% k8 Y6 F  R8 n. g        OutputID bc.Hash
5 k& b0 Q, I9 S2 x        SourceID bc.Hash
. w* E. o0 r% m2 T3 p7 I/ r        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
4 g/ T' ]& n. m" h        // AssetAmount with a nil AssetId.( L6 |8 H/ f8 A) O
        AssetID bc.AssetID( E! ^: X" n- ]5 a
        Amount  uint64
1 ?& p. q5 k7 K0 A+ ^7 T; i        SourcePos      uint64
8 M5 x* W  ^& n9 P3 E3 y        ControlProgram []byte
# V- p: S* V1 q        AccountID           string
( ]; Q8 S6 t! o+ A, G        Address             string; z6 ^1 p7 U2 x2 {; @' W( A' S
        ControlProgramIndex uint64* @6 x/ B# o, v1 W
        ValidHeight         uint64
* n! ~1 C. z: o& J' L" E        Change              bool: J1 N0 T9 ]6 z, |$ c
}9 Y  I% w% X9 j; I/ V- |
涉及utxo构造交易的相关字段说明如下:4 S7 h) m( H* ?" H+ |
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
6 [  @7 Y; F! Y9 H4 C+ M/ C2 n, v$ J& W' G0 M' R6 y
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)4 n6 s: z0 U7 ]
// BlockTx is the tx struct for getBlock func; B7 e; g0 c( E3 e2 ^% T, ]4 J$ J
type BlockTx struct {; M0 D% J8 i5 s5 y. {9 E/ L
        ID         bc.Hash                  `json:"id"`
# r' C1 O9 O* F$ z8 R7 g# h        Version    uint64                   `json:"version"`
$ f% ?: a) x0 i; l9 Y        Size       uint64                   `json:"size"`
( }/ G  W& w( f1 o        TimeRange  uint64                   `json:"time_range"`
9 G% `! {5 g# R' _; c/ V3 u. ?+ o        Inputs     []*query.AnnotatedInput  `json:"inputs"`
# k. V$ H# P# i2 S; ~        Outputs    []*query.AnnotatedOutput `json:"outputs"`
: v1 X2 `0 Y! t/ c        StatusFail bool                     `json:"status_fail"`! S: w. M7 _$ F: I+ L2 \. y
        MuxID      bc.Hash                  `json:"mux_id"`& x4 t- j" ~" _, y+ h
}4 l- [- B$ S5 ?+ y; v% @4 @
//AnnotatedOutput means an annotated transaction output.2 [8 v5 {" z4 A/ ^
type AnnotatedOutput struct {
+ n7 Z; e& ]  |# E: i2 `1 Z        Type            string             `json:"type"`
4 h: }7 W8 h& ~        OutputID        bc.Hash            `json:"id"`
7 F5 h* s2 Z4 P! G% a        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`7 P! z) v8 W! @, E* u5 }$ |- v- {: \
        Position        int                `json:"position"`
2 e! t6 |# M. U4 P* Y) y        AssetID         bc.AssetID         `json:"asset_id"`
! ?" _: R/ G7 S; j) V        AssetAlias      string             `json:"asset_alias,omitempty"`
8 B' w& L1 v7 T8 ]9 s& j/ a! G        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`5 ~' ~- s- \1 x$ Z6 z
        Amount          uint64             `json:"amount"`* R8 ]8 t) e1 W  q( r, P" n1 U* v
        AccountID       string             `json:"account_id,omitempty"`8 g) `! \& @# C6 N
        AccountAlias    string             `json:"account_alias,omitempty"`( `  Y$ t5 g/ _" O6 P9 d) e
        ControlProgram  chainjson.HexBytes `json:"control_program"`
0 C# ^8 `4 G7 s: K, B5 E        Address         string             `json:"address,omitempty"`
: I/ r) i8 N: h; c2 a  u; ^}
: I4 b4 @1 Y: R( E) `# Z% butxo跟get-block返回结果的字段对应关系如下:: c! A# x0 `2 k; D- w6 G
`SourceID`       - `json:"mux_id"`5 N8 I1 l3 \& {* d
`AssetID`        - `json:"asset_id"`; k+ q8 s- c/ X* P4 o
`Amount`         - `json:"amount"`" \# i7 t3 w9 V4 b
`SourcePos`      - `json:"position"`! U) K& o9 V" |' g$ W2 y4 L
`ControlProgram` - `json:"control_program"`
; B- S1 t5 {# ]+ B`Address`        - `json:"address,omitempty"`5 i% k; z6 P; G
4.通过utxo构造交易( x# K& J' y4 K9 H: S( a3 Z7 K
通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。
/ o; T* P& u8 n' k第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:
) ]' q8 e5 R7 t// UtxoToInputs convert an utxo to the txinput
9 `) N& Q3 i2 o. Vfunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {& K" Y9 t# p. ?! ?
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)5 x8 G5 q, p6 j& Y+ b
        sigInst := &txbuilder.SigningInstruction{}1 e2 p9 @# a& q: K1 W) }
        if u.Address == "" {. M5 a" v! F+ a3 s8 i1 i
                return txInput, sigInst, nil: `6 n* V' M' {3 R
        }* f( M5 P. z# t5 h# [0 s
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
. [/ n0 F9 m  j1 t4 G6 Z        if err != nil {& L2 r. J2 O: G4 ]( i; b. R3 R: T+ K
                return nil, nil, err
( j, u0 N# w+ _' V: w3 Y- T) ?        }0 S: T$ i  Q9 ]
        switch address.(type) {
: I( _0 F8 V( z0 H7 x4 j        case *common.AddressWitnessPubKeyHash:/ p# Y, C7 d3 T4 C5 Z
                derivedPK := xpubs[0].PublicKey()
: g3 b) g# ?2 \2 Y7 J                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
, J) T* y0 a& s% P# b. o2 w        case *common.AddressWitnessScriptHash:4 q9 q8 m  ?$ J, P, y
                derivedPKs := chainkd.XPubKeys(xpubs)( f; t2 z9 i: k4 B2 {5 G8 m
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
, G, R' A3 q- W* k                if err != nil {/ B; t! W2 G* K4 d1 `
                        return nil, nil, err
5 L+ v$ n/ i* [/ Q" b: B' W) F( S                }! y& a5 M0 o6 M: z
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))0 e9 J. O6 v8 L* ~( G" _
        default:
3 [0 R4 J/ K5 q. e5 t                return nil, nil, errors.New("unsupport address type")$ S1 Y$ U! ?4 W! X+ }' S  S. f! m( m
        }8 g% r2 `3 F/ f$ J5 o1 F, C
        return txInput, sigInst, nil5 f  y2 w0 \/ P
}* H# c+ F# t- L" y$ N0 v) k# ^
第二步,通过utxo构造交易输出TxOutput
* c) v9 L% a/ v( u7 l7 T该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
" O4 p( s: E% ^- @% E# H/ ?9 N// NewTxOutput create a new output struct5 `: `' ?" H" t# s! h# d& |
func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
' w) o0 i( A7 q        return &TxOutput{' H5 }3 B; U9 J$ r$ e0 Q2 @
                AssetVersion: 1,' V3 s9 G3 P7 a+ j# ]
                OutputCommitment: OutputCommitment{( n8 s6 D2 w7 {" F$ Q1 x$ c/ |
                        AssetAmount: bc.AssetAmount{
  e6 f+ [8 l- ]6 N                                AssetId: &assetID,) L1 Q! M$ t' j) w+ z& f
                                Amount:  amount,. A  r; Q8 r9 l$ h$ K8 D6 R
                        },
& |. I3 g% W( J6 ~9 P" A                        VMVersion:      1,
0 Z; Y& E& F4 H6 _) D                        ControlProgram: controlProgram,
2 E* O1 V$ Z* B8 t8 S' C2 x* h                },
. B7 C3 v9 f* Q* F/ l6 A5 w% b        }
4 Q* O9 }( U+ H3 X0 f2 z% z}
2 x) Y  k) ]9 q7 W" ?5.组合交易的input和output构成交易模板8 _: G2 P- `( L; k# E& J
通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
5 s" x* D8 I% b( _0 s) {type InputAndSigInst struct {
0 q3 o! g; T; `5 L! t        input *types.TxInput  t# K4 z1 Q8 |4 i: S
        sigInst *SigningInstruction4 h+ |1 _7 }: k! E9 B! W; z
}+ ~0 r7 Y7 T7 F: e+ r) e9 b
// Build build transactions with template
- i  F% c2 u# afunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {+ A0 r& \5 N8 R% i
        tpl := &Template{}
( x- I& ?9 k6 q% t8 W+ F8 l& W        tx := &types.TxData{}
% V1 ?0 O4 R) }) E        // Add all the built outputs.# o* G( n" `) Q/ I9 S
        tx.Outputs = append(tx.Outputs, outputs...)
' z$ X8 T- Q7 |. ]        // Add all the built inputs and their corresponding signing instructions.
& X" r8 Z$ Z6 Y        for _, in := range inputs {
9 O* v, u1 |# ~2 }$ X8 n. @9 ^+ m                // Empty signature arrays should be serialized as empty arrays, not null.2 n4 o0 h( R0 l; @
                in.sigInst.Position = uint32(len(inputs))
+ L4 V; {/ u; f5 x  o2 H                if in.sigInst.WitnessComponents == nil {- W* @+ A0 l: o7 m. ?
                        in.sigInst.WitnessComponents = []witnessComponent{}
! ]* t' B' y* e' S                }
2 c' }( m3 Z# G5 P                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)# X. l& v9 j' s# u) Z5 J
                tx.Inputs = append(tx.Inputs, in.input)- v9 Z$ t# d% {1 T& T
        }) [. u4 }3 ^6 c
        tpl.Transaction = types.NewTx(*tx)
1 ~: [0 D5 f! U! k8 i" J* B. V        return tpl, tx, nil, W8 Y2 w& o/ R$ L
}
$ I/ N" @% y  Y4 k( `2 J6.对构造的交易进行签名
3 F- z* X, y% `; _) T8 w账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)
5 M# A- n" ^; I; c% `4 z// Sign will try to sign all the witness
, P3 D, ~7 ]0 Q- V/ ^func Sign(tpl *Template, xprv chainkd.XPrv) error {) Z) s1 z0 J. K) Z
        for i, sigInst := range tpl.SigningInstructions {( H. b1 W% m/ m" n# [" Y3 O
                h := tpl.Hash(uint32(i)).Byte32()0 ~  H. F4 {) H+ @% y. W& P% i8 V
                sig := xprv.Sign(h[:]). a% H: O! Q" L
                rawTxSig := &RawTxSigWitness{- i2 _+ O; H" S" e5 O3 i4 `2 f& Q
                        Quorum: 1,
6 [% ?7 W* d, f1 v9 p, ]+ I' [                        Sigs:   []json.HexBytes{sig},/ S$ Y' F! [' T) J
                }
1 X& |7 C# |; D/ h: x, P                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)+ s' L' E. A  ^5 B- b* \4 o
        }
# F1 P% K7 B2 i- r        return materializeWitnesses(tpl)
+ U( o4 D, T5 H7 f2 \8 f( z}/ k0 F6 w, n8 B2 [6 K6 X
7.提交交易上链
9 N( g+ @( u7 E  {4 K0 V该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可
% m9 ~) R* f+ z- X: o: p5 B; q! V+ Q
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16