Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
1 L  z9 Z  A# c) [: U/ O" vGithub地址:https://github.com/Bytom/bytom
/ \. F' s0 x* Y) P$ S) e8 KGitee地址:https://gitee.com/BytomBlockchain/bytom" l7 P  J* [4 c! k5 K; t; Y0 h9 f
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。! ^" t; q4 A4 e; |9 G( q, j: `
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链- w/ }; i, [+ B: e0 H  t
; W: F% |( N$ {8 F* A
注意事项:- s2 [% G/ r! T% z; @4 L+ Q
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
* E# P/ k" V5 T1.创建私钥和公钥, _$ _2 r! ^' Y8 y) r3 `" D  t+ {
该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥' i8 Z4 G! ~: `# _
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {: s: b; ~  C  ~2 Y$ M& k0 {( m
        xprv, err = NewXPrv(r)3 {# ~7 m4 a/ n* z: @( b
        if err != nil {
3 O+ s# M2 M, R. H& Y( D                return' h0 @. r- s/ T. a5 G7 m8 b  H. d
        }
5 ?8 w; k- t6 D/ n        return xprv, xprv.XPub(), nil/ _7 x: O. K) U3 ?# W/ y. g& l0 O) l$ m
}0 {0 q5 i. I- Y  A
2.根据公钥创建接收对象
6 Z  I3 k+ K2 n/ K4 }% M接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:0 e6 }# l: S* Z! T. S( ]
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
3 ^* k$ ~$ T+ J, |! g& B5 T        pubKey := xpub.PublicKey()8 ^5 e: z, T4 g7 w3 Z
        pubHash := crypto.Ripemd160(pubKey); E+ v+ f8 }6 G+ C' G+ h3 `
        // TODO: pass different params due to config
6 j; g' Y7 J4 x% Z0 O* x6 T        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
8 k3 K( X! J2 F        if err != nil {
9 w( g& J) u$ D7 @                return nil, err9 O& |1 e! H, p  V: q, {) m; S5 A
        }
3 y. A2 N8 F- d3 p  _        control, err := vmutil.P2WPKHProgram([]byte(pubHash))" E. i2 |% B* o2 K7 M3 d6 [
        if err != nil {
  Q3 b6 J+ b' J% z) g                return nil, err0 {3 h2 C9 N+ I9 `) |* i: x
        }
" W1 I. G$ U7 e0 y- H        return &CtrlProgram{
5 s* Y9 r, j$ p) o7 w                Address:        address.EncodeAddress(),
& _) t8 e0 x. O' `: a% i$ w4 ~                ControlProgram: control,
6 e8 j2 _0 l5 F7 D/ K0 _4 u        }, nil
; h/ @  `; O- J! C- z& w3 m  P9 c/ A}
- E" v* T7 Y8 ^创建多签地址参考代码account/accounts.go#L294进行相应改造为:8 T$ e1 X5 _  H
func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {& s! t! s  |2 S) X# S8 H, U1 |
        derivedPKs := chainkd.XPubKeys(xpubs)
2 S  q8 X8 n5 |& n) Q        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))1 E2 z; _9 J9 X
        if err != nil {- Z) |0 z2 Q/ v" B8 u4 P
                return nil, err2 m; V3 C1 o/ A+ k0 b% n) b6 g
        }! t9 m4 K- O; t& o, g' K- d
        scriptHash := crypto.Sha256(signScript)' k7 S2 f0 G# A: q2 I4 c
        // TODO: pass different params due to config
" |9 k( j. T. _  Y) z% F        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
" A% J& v) s. @4 V- H; Q8 w        if err != nil {" m& e  H* x9 L1 N; L1 s( I
                return nil, err
: ]5 w" |3 x5 K4 f8 A! D        }
! ^1 l2 c7 y6 S        control, err := vmutil.P2WSHProgram(scriptHash)/ \. s: E# O. r' ^# \
        if err != nil {
4 M7 p2 |: S$ R1 T                return nil, err1 `1 h* J% M( C7 f9 j
        }
/ f1 W4 l# |) Y+ E1 K6 `) O+ I$ N        return &CtrlProgram{
0 F. v' p6 {% f8 V! g- j$ U7 h8 a                Address:        address.EncodeAddress(),4 l! `8 F" [2 l7 x, U) \
                ControlProgram: control,
" U8 ?' s  F2 @# @        }, nil; i" s" x2 X! V2 p. v6 a
}1 e5 K, r6 O& c. h
3.找到可花费的utxo
3 @2 U1 c3 w& D) D8 T- `& \7 G找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)& L) e. l2 F7 Q4 }2 R1 @
// UTXO describes an individual account utxo.: Q% Z* d6 t8 R8 a. `& B8 y) i3 A
type UTXO struct {
! }$ z  n, h' X' J        OutputID bc.Hash
' r5 J: Z& J8 x9 l3 ^6 E& V( T        SourceID bc.Hash
( p5 I- a& M; P/ X) ]; o( \+ }' D        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
" u. K) s  C- C/ ^/ {+ M# q        // AssetAmount with a nil AssetId.) |1 T; d  ]/ C* `3 G- R. Z
        AssetID bc.AssetID  N' V7 N6 n9 Z, T7 M1 b: y- C# O
        Amount  uint64
9 c, J- x( k; f: x        SourcePos      uint647 O2 f  Z! l6 }
        ControlProgram []byte
9 Q2 R! |/ J9 s1 ], V. s8 ?  ~        AccountID           string7 Y7 ?) g) n1 r
        Address             string0 ^+ U2 n4 R# D' Z# D% @
        ControlProgramIndex uint64( h: s6 F; j  y  T
        ValidHeight         uint64
' _. \, l9 R' J) m- h! e        Change              bool
4 Z0 Z: Q3 l1 p! ?& |}* Q! A6 H* `9 P/ R* L& r
涉及utxo构造交易的相关字段说明如下:
* L" r  @4 b. S# B) YSourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址2 K- X1 V0 I% m7 D& p' @
6 O! H+ ]8 @5 K: E; s9 b7 |
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)
0 B* G6 h: R4 d, N+ d  A2 D// BlockTx is the tx struct for getBlock func
; ~, K$ T7 k' Y! wtype BlockTx struct {
3 y7 S7 i4 g" X% N/ ^        ID         bc.Hash                  `json:"id"`
% V" G8 h% V- B' g        Version    uint64                   `json:"version"`
# y9 C$ i  ]; a2 Y        Size       uint64                   `json:"size"`1 R5 }  m0 e2 S2 L
        TimeRange  uint64                   `json:"time_range"`0 a- f' K& ?$ z7 `+ r
        Inputs     []*query.AnnotatedInput  `json:"inputs"`
/ J' G0 `# ^! c% s0 L. D* X        Outputs    []*query.AnnotatedOutput `json:"outputs"`
1 K* O' Z, J6 d/ Y. W& Y        StatusFail bool                     `json:"status_fail"`
- d8 Z% F) X- j        MuxID      bc.Hash                  `json:"mux_id"`$ F( j8 v# x# F9 t* P- ?* ]
}, l* N. R! u& y! n0 h! M2 m4 `
//AnnotatedOutput means an annotated transaction output.
% n, ?, X1 x2 g. p7 S  Jtype AnnotatedOutput struct {
+ R* @1 {( t# C% W7 P        Type            string             `json:"type"`
$ D' S/ Y$ o2 r! f5 g5 |        OutputID        bc.Hash            `json:"id"`. ?; E3 X9 M4 P3 i+ P, ^! G
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`/ D! l1 G3 _7 ~7 `
        Position        int                `json:"position"`! P: @& ]7 R- l" ?/ Y
        AssetID         bc.AssetID         `json:"asset_id"`2 ?# p' r3 Q+ p5 ]
        AssetAlias      string             `json:"asset_alias,omitempty"`
% h5 N# `( n$ ~; ]: @+ _* @0 P        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`" ]3 w, H2 B5 i6 L, ?$ ?  }4 w
        Amount          uint64             `json:"amount"`
6 u! |: h2 o6 F( t& Y4 U        AccountID       string             `json:"account_id,omitempty"`! J! Q7 |1 ]$ f* P& T& _# I
        AccountAlias    string             `json:"account_alias,omitempty"`
7 w) d! C8 ^. A7 B  [        ControlProgram  chainjson.HexBytes `json:"control_program"`, h5 [8 t, k* |  M0 @' q" Q, b
        Address         string             `json:"address,omitempty"`* ]* Q0 E: T7 ]7 q$ H0 `" g
}
! g4 l! e  R/ H1 autxo跟get-block返回结果的字段对应关系如下:8 v/ T8 X/ ?8 e8 Q9 M# Q
`SourceID`       - `json:"mux_id"`
) W  y& G: S) _" y2 g- G`AssetID`        - `json:"asset_id"`$ h$ J% J( n( T7 A
`Amount`         - `json:"amount"`
/ _* Z# i: W7 ?5 p`SourcePos`      - `json:"position"`3 O  |' C- y0 t7 _
`ControlProgram` - `json:"control_program"`
$ F: J  E6 o4 z& K# T2 ^: s" Y`Address`        - `json:"address,omitempty"`
2 l( Z7 L. A- ^/ v$ w) @2 T4.通过utxo构造交易. b! ^8 j/ c) P4 J8 }4 l  W- l
通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。
' i0 K( L1 K4 [0 r8 r第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:. Q0 `) C' n& ^1 k4 b! u$ t
// UtxoToInputs convert an utxo to the txinput
7 F1 u& M" J# c7 F/ P* @& afunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {
3 H9 ]+ o1 z3 |9 v3 E( a, W        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)
# H- b* C1 l' ?( c1 a" @        sigInst := &txbuilder.SigningInstruction{}
6 K7 r7 X/ |0 ~, F1 c        if u.Address == "" {9 Y3 S  x/ n# L
                return txInput, sigInst, nil
$ w6 s, C; ?1 M2 f) j, a4 ]4 v7 w8 w, R& _        }
  R0 O# D; [0 A" M        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)# c% c& A' W" e, v. C# C" x
        if err != nil {
3 }- M: C2 u. Z( ~8 ]( x! G( H                return nil, nil, err# @7 a; }6 u. `1 U2 i
        }
5 ^4 P  E% y0 y6 x' A        switch address.(type) {
' Q' Y1 B) `; r4 A4 W9 T& E        case *common.AddressWitnessPubKeyHash:0 G$ j3 w* q( {* f5 k. ~, C
                derivedPK := xpubs[0].PublicKey()  h/ ?8 y, F- z
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK))). V! V# d8 o& ^$ B  X; S8 E0 {
        case *common.AddressWitnessScriptHash:3 P; e* |* E/ r. y* g
                derivedPKs := chainkd.XPubKeys(xpubs)' f6 k- W' j9 E! D
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
2 B8 g: F) `7 X  \9 v. J6 e) f; c0 W) L                if err != nil {$ G3 w8 F3 J" x. h& S: W4 \
                        return nil, nil, err
6 I* b/ v7 a6 J                }
. H- m( J+ i0 @  o                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))
: C! J0 f1 d- Q% x2 g. x& M        default:7 N# g( D3 T) W
                return nil, nil, errors.New("unsupport address type")
4 N- M; j7 t+ n! A5 I        }
% h1 M  y" t6 H/ F        return txInput, sigInst, nil+ y3 M6 K1 {+ F: d! j
}$ p0 v  z  S5 M2 ^
第二步,通过utxo构造交易输出TxOutput, t5 A1 C: O" _1 Y
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
0 a( n9 X" L8 x9 D1 S// NewTxOutput create a new output struct
* a- J* \! K7 f6 R$ J+ \& X& \func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
( l' a8 z+ Y( R1 m, u1 ], ]        return &TxOutput{
/ @( e* D% M) J                AssetVersion: 1,
; C2 E. X( q; {: i                OutputCommitment: OutputCommitment{1 E: o! _# D! v  i. k, T
                        AssetAmount: bc.AssetAmount{
, T5 f, N& G/ M5 c                                AssetId: &assetID,
) |. E7 j* f9 }0 d                                Amount:  amount,$ `! X9 Q: w2 H% @% p# ~  X
                        },
' p) w0 X# j6 s, {: s                        VMVersion:      1,
& k3 E1 l! Y+ ]8 J* k                        ControlProgram: controlProgram,& W: N  E! d; a0 O8 J( Z, l
                },1 ?+ D7 K* G# [* I+ n
        }
' i1 `1 P2 L' N* Q5 B* s}6 j! l% p4 ?$ Y1 i) n  A2 Y! \: S+ \0 S
5.组合交易的input和output构成交易模板
1 T: ^6 b' G0 w4 R4 j  F$ D1 l% D通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
- F& W. `" ?/ W1 Y5 B" N9 stype InputAndSigInst struct {
  M  Y- q7 b: o( R! Z% n" z9 T( j        input *types.TxInput% _4 P8 g# h7 ]% k6 v" y$ k% h6 m
        sigInst *SigningInstruction# B9 ^) W2 q$ U* h& O( H
}( c6 f0 s6 X2 u: i0 `5 `; l
// Build build transactions with template7 n" g: ]  M. ?2 J! y3 ]) N- y( c
func BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
0 m+ u5 O; I* Q+ [: i2 O        tpl := &Template{}  C  A0 k! h, I. g) }
        tx := &types.TxData{}2 O& x$ [3 v7 s7 A- c
        // Add all the built outputs.4 @! Z* ?- v+ a3 M3 P% w0 ]/ T
        tx.Outputs = append(tx.Outputs, outputs...)+ ?- Z; T! [. P
        // Add all the built inputs and their corresponding signing instructions.' N( q& O+ G  l4 g
        for _, in := range inputs {
+ Y- ?) ?% `: m0 f                // Empty signature arrays should be serialized as empty arrays, not null.7 F1 E3 |- `. `) m) V$ X
                in.sigInst.Position = uint32(len(inputs))1 p+ f+ ]% B- H% y  b
                if in.sigInst.WitnessComponents == nil {/ \3 K$ M) Z. d4 q1 V, f; T
                        in.sigInst.WitnessComponents = []witnessComponent{}6 T! d1 u, K: P. X5 T3 w6 j* b: Q$ L
                }
+ _: Y0 P( L3 C9 F# l                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)+ I; T$ a. G5 k2 _
                tx.Inputs = append(tx.Inputs, in.input)
' a( ?3 y/ `3 L7 q; g3 U+ Z% H        }
, {1 d, {# {2 B8 s0 @        tpl.Transaction = types.NewTx(*tx)
5 v$ A6 `$ h! n6 [6 Y1 P        return tpl, tx, nil9 a" B/ ^% I. H: N0 Q& ~' |
}0 [) Y' |. D: z1 B
6.对构造的交易进行签名
& N# ]8 E1 d+ K) Z账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)
1 w5 x. t. Z( H// Sign will try to sign all the witness
/ C4 S9 D9 Z  r1 |' B0 @! S6 ?func Sign(tpl *Template, xprv chainkd.XPrv) error {
0 T- \  L. k" h+ F        for i, sigInst := range tpl.SigningInstructions {
# J: S6 {* @9 O1 F                h := tpl.Hash(uint32(i)).Byte32()6 C" S. L, \( X( E) C* T: e) O5 b$ U
                sig := xprv.Sign(h[:])
- L. Z  a& ~* j, @; P" i5 T                rawTxSig := &RawTxSigWitness{
0 M) @+ J) L. |                        Quorum: 1,+ A+ M! z7 ~6 b5 {
                        Sigs:   []json.HexBytes{sig},' o- G2 o, F- ^% |, _* k
                }% D% m) P  B# O( _" Z2 K
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
! W2 y8 f- S4 S* s& e( S0 B& _        }! p8 L5 {5 Q, U0 ]* z, S  e/ A/ j
        return materializeWitnesses(tpl)
4 z& i! [. i# y) u' J3 s; i' z7 F}- T0 h+ x( R. ?, G+ {: y' G: M" U- |
7.提交交易上链
1 A8 t! i9 |* T0 N6 g3 i' v该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可: H; j% N" N6 n. |4 _+ T# B
6 @* W/ @5 s: K% j4 T% J
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16