Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:$ N5 i( p3 t; s5 t7 A
Github地址:https://github.com/Bytom/bytom9 E4 V# B, T! `
Gitee地址:https://gitee.com/BytomBlockchain/bytom
9 U% ~0 X7 Q( x- x) A' u! B0 x7 Q该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。
# Y) H( c1 A9 |1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链
" U: s; \9 b) O& {
% ^6 ?% p1 G/ J( z, p注意事项:
7 z1 G/ ?' u/ |+ s以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L2553 a1 z( r. E3 h
1.创建私钥和公钥
! A, t7 [, A# `. N# [/ K% p该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥0 ]( m1 `1 O( G
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {: I1 D0 O( [# p! h0 K
        xprv, err = NewXPrv(r)6 U# z4 P. P% F
        if err != nil {
2 c& [, H0 k  x( W. o0 n                return7 x6 s5 e2 m/ j2 _) Q7 w
        }7 a" V6 u1 t9 u  L1 c9 {
        return xprv, xprv.XPub(), nil
' b; c( ]8 L. R/ [" E}
9 O5 s; ^& l% q2 V2.根据公钥创建接收对象, h! z6 U3 A6 l+ }) N, \
接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
0 R* K, F9 L$ _# b' s* efunc (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
3 k% \' ?5 N8 @        pubKey := xpub.PublicKey()2 i: h' _  F; t% \
        pubHash := crypto.Ripemd160(pubKey)
" v/ {3 K% p) S$ @4 C) k3 V        // TODO: pass different params due to config
# U" G) W9 z& H, N' E1 ]$ `7 `/ `        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)) E! D! U- _9 j9 F, y: r, F7 C' Q' u
        if err != nil {
4 g5 k' s3 f0 S! L3 G$ L" {) t                return nil, err
3 r7 u% ^( W( U, k2 |# I* e        }1 n1 M9 B9 g0 x& C
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
3 |0 i" p7 x6 r0 ]        if err != nil {
! d- S( U5 r, D5 a. c0 _6 T0 b; |                return nil, err* P5 b3 o5 H9 U1 M/ ~2 s
        }3 m7 Y! \0 S. r. o/ e" F
        return &CtrlProgram{
& e' }. Q7 N9 v3 G# T+ h" Q- `                Address:        address.EncodeAddress(),
3 e( V0 s" X4 D- K! g7 H( M                ControlProgram: control,# D: N+ F# N! ~$ s
        }, nil' ~7 _' }. h) Z, g
}+ _. Y% g0 f" X' S
创建多签地址参考代码account/accounts.go#L294进行相应改造为:8 n3 u' G4 T! x9 R2 G
func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {
& c, E  p8 h3 w# Q% j8 c# u        derivedPKs := chainkd.XPubKeys(xpubs)% B# I( f: n( b9 c
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs)). {, T& J5 G0 Z+ g' b
        if err != nil {2 G( W/ O4 V" A. U
                return nil, err
3 m9 Z" w3 [6 {0 v2 A1 R! ~        }
! d; S0 n1 l# f" J        scriptHash := crypto.Sha256(signScript)% S2 C: W) ~: n0 S/ ?% \9 x, R
        // TODO: pass different params due to config
5 q- ^' o# ^/ ^* c! ^9 b        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)9 j  e: _- ?- j6 M  O! R! b
        if err != nil {
% B- B: a- s' X9 O1 g! J6 N                return nil, err; A( j" {/ n6 U4 y, x
        }
* K& O; y9 y2 _2 F* E4 T        control, err := vmutil.P2WSHProgram(scriptHash)" w3 J8 K  t) S" h7 h
        if err != nil {. r  A3 ]* \3 c: _& h) a2 ]2 S
                return nil, err9 z3 g0 _7 ~* t" U2 @+ O+ t* c
        }
6 f: G( h! M* c, B* z4 Q/ U; q        return &CtrlProgram{. a* A+ Y4 R7 B- ^# O: q+ v
                Address:        address.EncodeAddress(),
5 j( ?6 h$ U: n9 O9 W) Z! B- u                ControlProgram: control,8 z- r! c8 F. g8 q6 J) R
        }, nil
9 p# e  v& s3 L4 q}
0 M* c7 @: m, T9 R: B$ t9 o! E3.找到可花费的utxo7 z; N+ ^6 B& L9 Y& g  b/ _+ }6 [9 f, i
找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)
/ d& N  B% z: \: A& l$ @: t// UTXO describes an individual account utxo.' X9 P) b5 o. |
type UTXO struct {
9 K5 G2 }1 ~5 }) L        OutputID bc.Hash
7 [+ W# @( C* q. P" f" `        SourceID bc.Hash
2 O# {& y2 `+ d- }+ B9 M& z        // Avoiding AssetAmount here so that new(utxo) doesn't produce an1 k! p$ }, u; P; N; B. W6 N; x
        // AssetAmount with a nil AssetId.
7 F# H# v5 i$ T: o3 V: q        AssetID bc.AssetID
3 a7 W1 ]4 {: r1 Z+ {, z        Amount  uint64
* k# k2 L, l$ \9 Q# v4 }  ]        SourcePos      uint64, Q% j$ z/ j* V: b! _7 {: J
        ControlProgram []byte
6 j' _# p# }: O% h  V6 u$ T        AccountID           string
5 _4 o" P3 n7 A% ~" {+ R        Address             string
  w8 g5 o! V( A9 P! Z) H        ControlProgramIndex uint643 @  l& t. a- [2 X5 B
        ValidHeight         uint64* `/ Q2 `1 h$ B7 r% \8 f
        Change              bool4 |* p  l' V, f9 \/ R0 f! @: z/ f
}
" Q# n" M; l( {# O* e涉及utxo构造交易的相关字段说明如下:
6 Y' W5 i/ H# {4 _; USourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址
7 u' q; E- [# [2 K: o, }; r
4 ~+ p2 L* x  t( W+ K上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)) \1 i6 K3 a- w$ M" L6 V% ]+ B! N
// BlockTx is the tx struct for getBlock func
( w* v& g. y% _/ @& F9 Ltype BlockTx struct {# o" O1 }2 [4 |
        ID         bc.Hash                  `json:"id"`
, [0 y. V/ i  I2 I, @0 f        Version    uint64                   `json:"version"`/ `6 b8 L3 C# d5 X2 R
        Size       uint64                   `json:"size"`  t/ Z  D* n$ M
        TimeRange  uint64                   `json:"time_range"`
3 d, n7 _( D0 Z+ |; @( B, Q8 \) f( c        Inputs     []*query.AnnotatedInput  `json:"inputs"`/ H1 J( a: K' O+ a3 J1 K; q
        Outputs    []*query.AnnotatedOutput `json:"outputs"`: I3 L; I  E+ g8 e
        StatusFail bool                     `json:"status_fail"`+ |, m" P: H5 k5 N6 F' \$ u
        MuxID      bc.Hash                  `json:"mux_id"`, n. @9 |/ ]4 I5 D9 R* A% `' X
}
3 P0 |7 D4 u) @7 b* J8 T//AnnotatedOutput means an annotated transaction output.
  s  \: F. N' ytype AnnotatedOutput struct {
4 z' P# p  }# W' z* F. J        Type            string             `json:"type"`
+ T2 k: D+ D& I3 q  E% Z  M, r4 R        OutputID        bc.Hash            `json:"id"`% h' L. z. `" ?# c
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
# n. x! c- N8 ~$ v, s        Position        int                `json:"position"`
$ H7 P1 X0 L7 k1 `2 D        AssetID         bc.AssetID         `json:"asset_id"`% v9 h  P: m9 E: a9 R: H5 _# p
        AssetAlias      string             `json:"asset_alias,omitempty"`
' |" Z  N/ y2 m7 \$ N' V        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
: J$ f; H+ a) b        Amount          uint64             `json:"amount"`
/ g0 r& E8 t% P5 C; s+ j        AccountID       string             `json:"account_id,omitempty"`1 J" y3 a0 d" @' o" D4 {
        AccountAlias    string             `json:"account_alias,omitempty"`; ]& M3 v# a! B) X
        ControlProgram  chainjson.HexBytes `json:"control_program"`( J7 j3 X5 H1 y- `9 ]5 H4 p
        Address         string             `json:"address,omitempty"`8 c! x: L5 ?2 W$ ~8 C1 c
}
5 \# ?9 Z" O: S- \/ F) O+ \  Putxo跟get-block返回结果的字段对应关系如下:2 C1 }( B* Z% b3 c: E
`SourceID`       - `json:"mux_id"`
8 f  r$ @" q4 h! Q+ ?: A. f$ M`AssetID`        - `json:"asset_id"`
# C. f6 b1 q; X4 y8 L* @`Amount`         - `json:"amount"`
6 }5 Y$ O9 C! v`SourcePos`      - `json:"position"`
. {. O' ~- D3 v2 T& @`ControlProgram` - `json:"control_program"`0 C% k& Y* j3 A8 Q0 a% U: p7 P
`Address`        - `json:"address,omitempty"`9 {1 U( D9 s5 I, j
4.通过utxo构造交易& L( _! u' _4 a0 l* i
通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。4 _& k: H( a% b5 r. c2 e+ ~
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:; S9 y1 T/ V0 h, V5 O
// UtxoToInputs convert an utxo to the txinput
+ [" J2 f; Q/ O  }' Z0 `- |func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {+ K4 f, C4 _! n, J5 x' P
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
! {7 s; Q9 i) {& |1 c1 l        sigInst := &txbuilder.SigningInstruction{}8 `  U; o7 B) V0 w
        if u.Address == "" {; H" E) |; R0 I. o$ s7 E
                return txInput, sigInst, nil0 V# {% W  D/ v3 Y: Z1 e
        }
& v/ s  ~9 O% c" j- Z        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams): ^& s3 ?. V7 m$ M- G% |# P
        if err != nil {
" r2 Y) f/ Y* Q, A6 G3 M                return nil, nil, err. s% |" p* O, o2 p" w* e, s: {$ G
        }8 b- r, a6 r4 [7 n( g+ }
        switch address.(type) {( w8 M0 h9 p: Q! z
        case *common.AddressWitnessPubKeyHash:+ ]* k6 \: X( }( ]* X
                derivedPK := xpubs[0].PublicKey()
6 T$ ~$ [/ X' S/ @) F+ d                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
/ U) Z$ u8 g5 P+ b4 t: q        case *common.AddressWitnessScriptHash:
" }& z9 n7 o% O3 A0 C' R' u4 D                derivedPKs := chainkd.XPubKeys(xpubs): X$ p* \. X0 e) Z/ O6 Y
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
, I$ G" K3 [8 ^9 D8 l) S1 c% F                if err != nil {
7 k* m) @$ A& B2 B1 A6 J% G                        return nil, nil, err0 H. l: s6 {" L3 x) V
                }" S3 ]; r/ y* r) T1 T
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
2 p% a& W+ z( p5 b7 b' M% ~        default:# Z* q- q) T. g9 s; b, `
                return nil, nil, errors.New("unsupport address type")
7 Q* G0 E  C0 g; E+ v7 \4 V        }
6 ^5 G$ S1 W4 r( ~) S4 L        return txInput, sigInst, nil& k/ b7 S1 y  y* `/ R! _( a
}
4 ~8 L! ]- \/ k6 h第二步,通过utxo构造交易输出TxOutput1 Q0 U. m6 O% l
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:' _' d) Y2 i0 _4 d7 u  j. N
// NewTxOutput create a new output struct
( O- |( ^- `0 F. jfunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {1 w  K9 @) K6 s0 n; [
        return &TxOutput{2 A) I/ n. v. F' J- D
                AssetVersion: 1,7 U4 i$ E6 z/ H
                OutputCommitment: OutputCommitment{
. W8 U$ b; B" e8 F% P0 y                        AssetAmount: bc.AssetAmount{4 d, z* I; @# f3 x" M1 r( X. Y: q
                                AssetId: &assetID,' [" ~: X& d8 L% x8 L1 x! D
                                Amount:  amount,* Z4 z2 F! k8 H7 G
                        },
! Q: i0 ?: y7 Y- O7 v                        VMVersion:      1,4 _5 c% h0 ^6 ]6 g" }) ?
                        ControlProgram: controlProgram,
# Q, f: U9 \" P: R3 B) o9 B                },
9 ?  _* C1 |* ~, j        }
% y5 h3 T5 F# M6 p3 J/ P4 @}
- b2 y$ j3 x8 a5.组合交易的input和output构成交易模板
- d& k. U% {! e9 N通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
0 Y! G; A3 Q, z$ e% U; }6 w5 gtype InputAndSigInst struct {+ w; O2 }4 d9 N+ A/ G' \
        input *types.TxInput
( ^: R# f# M% B6 j        sigInst *SigningInstruction( W( R+ d: P7 X' Q. ~/ a
}
) l/ H4 T5 r/ w4 |1 q' _9 L// Build build transactions with template
) X/ O) l/ x8 w4 `, d& Yfunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
1 x: _  ^% E9 t- w) s/ K        tpl := &Template{}9 Y  g1 t7 c/ ~/ [& p0 Y" O8 ]
        tx := &types.TxData{}% `: z& \: q% z  K1 @" ]! o
        // Add all the built outputs.' o/ x+ m$ ^3 U# c2 r! N+ w
        tx.Outputs = append(tx.Outputs, outputs...)
% `  \; h) }# h$ w& {' K        // Add all the built inputs and their corresponding signing instructions.
$ @, a+ n( p) p9 T        for _, in := range inputs {7 h/ X& j: b9 C7 V+ e
                // Empty signature arrays should be serialized as empty arrays, not null.5 _/ {& {3 A3 }. q. t7 J
                in.sigInst.Position = uint32(len(inputs))
0 N. W7 z, B+ m9 ]; w; x' k: l                if in.sigInst.WitnessComponents == nil {
: F: J) L6 J( E% H5 w1 T7 r! _# [                        in.sigInst.WitnessComponents = []witnessComponent{}+ T  N% w  w4 l9 W/ U* b( J
                }
8 {9 f: M( t6 B: P, `( U                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
! J6 f  Y4 F2 ^6 b. k3 Q3 o                tx.Inputs = append(tx.Inputs, in.input)
) s' H( `  [$ U        }
+ L, H6 I+ {1 Q' V7 V        tpl.Transaction = types.NewTx(*tx)5 ]: \2 t- m$ I" X7 U+ K. q7 f& S3 J  A7 v
        return tpl, tx, nil
$ \+ M1 p# @" S& v}) K6 n3 H9 q$ l
6.对构造的交易进行签名
/ {8 r7 C6 p+ h  Q% @账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)
1 t, {( z& j, W$ z0 G// Sign will try to sign all the witness& j* S( v8 c+ k% m
func Sign(tpl *Template, xprv chainkd.XPrv) error {
" o) c, c" u! y        for i, sigInst := range tpl.SigningInstructions {
: s0 A9 `& D; g! h" D4 i& T                h := tpl.Hash(uint32(i)).Byte32()
& t5 M% ?9 P& v) F5 m- d1 C- f                sig := xprv.Sign(h[:])
) H. n0 ]# _* l, X                rawTxSig := &RawTxSigWitness{1 J0 n. y1 a4 q8 @- _
                        Quorum: 1,/ B+ j2 |3 }) S5 e
                        Sigs:   []json.HexBytes{sig},
. s0 T0 Q- [3 O                }
1 t% G7 p- }* o' t. R- s% {- \' A                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
! }' T. h9 p- s( X7 x; L4 H        }" f0 ]/ Z- S5 C7 M
        return materializeWitnesses(tpl)
. P- D9 K! b5 k1 \/ F}) h9 Z5 n& d! Y8 V: Z  ^
7.提交交易上链
3 [5 B2 w; E) s9 N- p& R该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可
" x' k' i6 h# U. Y% @1 J7 L5 T6 W! S$ W" R4 F' {. ?9 e
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16