Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:
' f6 [# w) c! oGithub地址:https://github.com/Bytom/bytom
6 H5 Y  T7 C5 I; VGitee地址:https://gitee.com/BytomBlockchain/bytom4 L+ {* O& c! G- F. G3 E" J
该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。
" `+ d" M0 L, p: |% E1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链7 L  d% u9 Q7 V3 D7 A7 s  ~% z9 c
$ O# S# G- ?& l7 X& \( Q
注意事项:. R1 u% q! r  \2 d
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
8 s+ X- [4 o# f3 B+ ?! Y8 Z1.创建私钥和公钥
* K" A  N+ B7 |该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥: ~! L' s# u1 r3 }" V
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {7 }& ?. g8 A' b; C
        xprv, err = NewXPrv(r)
, F! e: w# h1 Y' O        if err != nil {) L+ N) e* l" |+ L( P
                return2 I: \( a$ X6 ^# [0 H& n
        }0 S; g2 w& d$ I+ a% k7 u
        return xprv, xprv.XPub(), nil9 ?+ o( J7 n* x0 _3 B
}7 }' O1 _' U, Y7 V
2.根据公钥创建接收对象
5 v. v' X2 T; G  a接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:
# m2 U6 B/ i$ Lfunc (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {
8 x9 W7 u( S* }. y0 b  I1 c        pubKey := xpub.PublicKey()& |8 H* O# d* K# ]1 |; ?5 e+ B
        pubHash := crypto.Ripemd160(pubKey)7 I2 c8 T1 U- g. l6 U% y. j& K
        // TODO: pass different params due to config
: x8 h6 {; S+ k0 P, b        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
. v% C' c0 R. s. n- w        if err != nil {2 ?0 c6 l( r5 W0 f
                return nil, err% P2 r1 A4 _  V
        }
; g5 _' H9 \! N% W4 c9 o* T        control, err := vmutil.P2WPKHProgram([]byte(pubHash))
' s5 |: Z$ \. z6 P0 @; F/ u        if err != nil {0 d5 M  m" m1 q* c9 K' d# C/ B
                return nil, err1 H) h3 L4 V' [! {
        }
5 k+ j9 ~8 I$ B" s; C        return &CtrlProgram{
/ G) Z2 @; F, t0 q5 a. {* @7 U                Address:        address.EncodeAddress(),
4 k0 J7 w1 ~/ u3 P6 z                ControlProgram: control,/ a7 e+ ~$ ~# {$ c! E
        }, nil
+ I7 e- u/ |6 ]6 R3 C7 \}4 ?7 m1 z2 e2 K$ S) ?6 e" U1 e
创建多签地址参考代码account/accounts.go#L294进行相应改造为:
0 W1 m6 e4 R, j5 K9 gfunc (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {
; O/ Y8 J. K5 P" \" z        derivedPKs := chainkd.XPubKeys(xpubs)
/ l) m0 C! v9 z7 I        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))3 c. @. \6 P. s' y  s, s8 c+ |8 O
        if err != nil {/ R& ]# {: C! Q: |  t& [
                return nil, err( c/ f% a) }( A( p) L* i
        }
+ P* `  z+ w* N8 o; k7 c$ y        scriptHash := crypto.Sha256(signScript)
9 B0 B* A4 x( f6 l3 h' O2 o' ?" D        // TODO: pass different params due to config% a" I& M: y+ g3 f7 U" q
        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
( m( @$ o/ W! S. X: x, f. }        if err != nil {2 l4 y& M/ N" u- D- I% c
                return nil, err
' i7 M% m- \: d        }
7 C* O; A9 Z  R4 T+ M  ~$ @$ S        control, err := vmutil.P2WSHProgram(scriptHash); }# G4 B7 Y9 v1 Z$ Q' f
        if err != nil {8 @2 [! I9 P/ w8 k* g1 e) ^, N- {
                return nil, err& D6 Y" Y4 x% h+ N
        }6 O; g- x1 [" s4 P- q$ C. V
        return &CtrlProgram{
0 I0 F+ T' Z2 W( M; D- q                Address:        address.EncodeAddress(),, z% `8 M0 C3 f: Y: v& [4 n
                ControlProgram: control,
- u# K4 v5 L3 e0 A% E$ e        }, nil4 }2 K3 V% O" P' c0 W& N7 \# Q
}0 E9 v/ b" p. |7 f" m; ^) U  B, [
3.找到可花费的utxo; S/ ^, P, J% G) u8 A( M* k1 {
找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)! Z; P3 u7 r. X
// UTXO describes an individual account utxo.
; j, D: z9 G* B/ }  Z# Q0 Wtype UTXO struct {
' e7 |% A0 ^* U- z7 i8 ]        OutputID bc.Hash1 S* x3 i! }. v/ W( d+ a
        SourceID bc.Hash
% z0 i1 m" H1 t- f/ Z$ w5 ^  H6 D        // Avoiding AssetAmount here so that new(utxo) doesn't produce an
1 y9 F! m4 V& y! e4 U& C        // AssetAmount with a nil AssetId.2 l( T% ]/ P7 c4 M& b9 F
        AssetID bc.AssetID( i: a( D9 a- Y/ L: ?1 K  D6 M
        Amount  uint64' u% h/ x0 l( E1 a5 s% \. @$ E
        SourcePos      uint641 V! M) u9 s- T7 H$ J5 w6 y6 _) S- U4 J
        ControlProgram []byte
* U" s8 \8 w$ {  y+ Q& h( ], K( L* t        AccountID           string  @4 c' \. C, z# o( D* T
        Address             string
8 ~/ E: K& S" N) j# m        ControlProgramIndex uint64
( _7 C/ f1 d1 d& \& q        ValidHeight         uint64
1 R7 ]. {4 Z& N6 A) U4 f0 e        Change              bool
) f' O: X4 \: m/ Z0 `8 s" Y' L  Y}4 ?! D4 R& k) a/ |  r$ u
涉及utxo构造交易的相关字段说明如下:# z- V$ P; c) k2 ~- J
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址# f8 L  I) t' a5 z2 c$ ~$ J
4 Z; y) O- q! W; H0 }
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)% i6 b0 k  g1 ^0 }
// BlockTx is the tx struct for getBlock func# e2 E4 s% M2 E* E$ ~
type BlockTx struct {
/ t( d  H; r! e  |        ID         bc.Hash                  `json:"id"`! z5 a" n2 l5 \# ]4 }
        Version    uint64                   `json:"version"`6 U, f- L- ]8 M# {
        Size       uint64                   `json:"size"`) e* {7 [3 Z" Z
        TimeRange  uint64                   `json:"time_range"`: S+ L- s  i% j7 f& R
        Inputs     []*query.AnnotatedInput  `json:"inputs"`
# \! j: g% k3 Z. o8 S; u        Outputs    []*query.AnnotatedOutput `json:"outputs"`
$ `2 D7 }6 v; U, Y; I: R! m4 J        StatusFail bool                     `json:"status_fail"`$ ^! g( k6 u; m* Z  R- i- n( ]2 E
        MuxID      bc.Hash                  `json:"mux_id"`- u* V, x6 d+ y; o, j
}9 f! V- E4 u: t9 v& Y4 ]* L/ B
//AnnotatedOutput means an annotated transaction output." n: s7 \7 `. e) E3 X
type AnnotatedOutput struct {8 M2 Y6 }/ P+ Y, }( S7 I
        Type            string             `json:"type"`
5 h& \& ]; c: f6 i8 j2 C9 O        OutputID        bc.Hash            `json:"id"`
7 K4 j  P4 s) o3 ]1 q' U        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`8 f8 G& ]6 {; s% A
        Position        int                `json:"position"`
4 V+ @2 u8 y0 @* \2 d        AssetID         bc.AssetID         `json:"asset_id"`
" r+ o, f# {2 [# y        AssetAlias      string             `json:"asset_alias,omitempty"`. C# T5 M( O8 {. r4 W$ a) u
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
. X2 g1 }7 }- s2 _0 c+ \9 ^; l        Amount          uint64             `json:"amount"`
/ ^1 e6 k; Y- h: u) x) _. T% V        AccountID       string             `json:"account_id,omitempty"`
, ~7 m' l8 a' `1 e- [/ r        AccountAlias    string             `json:"account_alias,omitempty"`
! P6 j; Z9 `; S, e* z" I/ [        ControlProgram  chainjson.HexBytes `json:"control_program"`
3 L) p) ?( b9 O1 y* _& R; d        Address         string             `json:"address,omitempty"`
% @4 d' U8 k  f3 h}
7 }0 l7 @% q( }4 e: X7 E6 i* [utxo跟get-block返回结果的字段对应关系如下:
' |( f" i# B' F* J; ~  Z' ^6 T' s`SourceID`       - `json:"mux_id"`, B8 V& B. k; W9 @( c
`AssetID`        - `json:"asset_id"`8 p, u' L5 X; T7 f
`Amount`         - `json:"amount"`+ n7 d; Y0 }4 F! X) B! }6 G
`SourcePos`      - `json:"position"`
2 _% P: J& Q' q7 J; L. b3 V6 g`ControlProgram` - `json:"control_program"`/ p3 b+ I2 Q4 c6 k. q; w
`Address`        - `json:"address,omitempty"`8 R4 U/ d* C  ~+ C. c: C
4.通过utxo构造交易
0 P1 y" f8 `4 l1 U  N/ Y$ |+ l- }通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。' K3 [+ V4 l' v/ B; F
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:  J0 R6 u' d1 J) U" Q6 k0 F+ Z- j
// UtxoToInputs convert an utxo to the txinput
/ t/ K9 ?1 b, ifunc UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {1 e- `2 i+ W& w) Y. O
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)  b; B4 m  m1 U: ^! ^+ L
        sigInst := &txbuilder.SigningInstruction{}& h! b; S# f6 T) B1 G
        if u.Address == "" {
# C* ]+ n  ?5 i. `. l( p5 F5 H                return txInput, sigInst, nil$ F. _5 I. K1 O* F3 e
        }
- l" s' n. @: Q$ {  Y        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)
7 o' R+ C' f% s7 ^        if err != nil {
7 Z' `/ ?9 F4 E0 M3 e1 y                return nil, nil, err9 r6 e: G; G, Q7 p. J% ~5 \/ z
        }
. c2 Q# Y- n; x        switch address.(type) {
  _7 w1 {7 V9 h0 G+ {        case *common.AddressWitnessPubKeyHash:3 A1 U7 m6 N0 H, j8 I. z( u
                derivedPK := xpubs[0].PublicKey()$ x0 s7 D) u; Q* D
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))
/ i! G% l) p  ~3 R/ D        case *common.AddressWitnessScriptHash:
8 N6 v" o4 k7 D3 w/ a3 _& \1 A                derivedPKs := chainkd.XPubKeys(xpubs)
7 X* U0 q$ O( q                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs)); i/ q& r* @) ^. a) r" P% |
                if err != nil {  L- K  u! d3 S& y0 Z. L% z9 b3 I9 {
                        return nil, nil, err9 _4 c7 n% S* {& F0 B% S4 G+ V  U5 y
                }
" R. i; ?+ W' ]8 _2 r! u; [                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))9 k# _' u/ L! U9 N9 b" {
        default:, |3 z! ~- {" O) ]% H
                return nil, nil, errors.New("unsupport address type")
; h, \" z+ B8 h4 r/ A        }5 S7 S1 x4 p* ?- ]7 K' |% M
        return txInput, sigInst, nil
$ ]  E. N* Y; i}0 w* O: x" f- [& b
第二步,通过utxo构造交易输出TxOutput
" @, Y. ]+ t/ x5 `  B该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:% @& p# d" d9 F3 W# }% v
// NewTxOutput create a new output struct
8 v8 y2 S2 z4 a8 A7 l% j1 t. P: Yfunc NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
9 z; X" G0 x" x6 C        return &TxOutput{
" h% n+ a' b! ?2 h, W+ P- V; a                AssetVersion: 1,6 r9 ^  ^- L# D9 T* S
                OutputCommitment: OutputCommitment{
' f: a" l2 n! P" v9 g% p8 G                        AssetAmount: bc.AssetAmount{
7 U: E6 F2 \- H. B                                AssetId: &assetID,
* L/ s6 ?  \( O2 N) U' O$ T                                Amount:  amount,
8 U; O  W2 \# \& L+ e                        },# W% {$ B$ i" O; m0 d
                        VMVersion:      1,
+ ~5 p2 Y. y- V" t6 Y4 K/ F                        ControlProgram: controlProgram,. Q2 O' b( Q, F; T0 b
                },
4 Z6 t+ d3 F0 k2 G& Q        }4 L) \: p1 r6 b- u  g
}0 {( ?0 w5 K% r* Q9 ~* [; n5 G
5.组合交易的input和output构成交易模板4 L: a: K5 J. G) c, [
通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:
% R$ M8 m$ L# @; jtype InputAndSigInst struct {; }3 F2 ~3 Z. K7 J7 d
        input *types.TxInput
; W) l) M7 w( k        sigInst *SigningInstruction
/ v: Y3 q3 K# ~& m8 l}: b$ e2 o+ ?- o# A2 Z: g
// Build build transactions with template& Y# g) O. @) z6 Z  C1 I# t
func BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {
9 g  X' `+ z2 h, L9 t% f        tpl := &Template{}% t6 ^, o; o; H
        tx := &types.TxData{}1 H0 e$ W2 }6 S6 H5 p5 a
        // Add all the built outputs.7 k$ C4 U# t$ ?# f3 Z+ p8 n6 ~
        tx.Outputs = append(tx.Outputs, outputs...)
( Z( j1 c3 i: `5 N" Z/ @0 [        // Add all the built inputs and their corresponding signing instructions.
8 I0 o% I3 k/ B3 b$ g$ d        for _, in := range inputs {" s  y, c0 [. U6 D! F5 h- [- i
                // Empty signature arrays should be serialized as empty arrays, not null.' `6 R; g5 d" ^, T( Z) S* C) q& d
                in.sigInst.Position = uint32(len(inputs))7 M, M- X" }8 s) ?9 x* A$ q
                if in.sigInst.WitnessComponents == nil {5 D  Q5 F7 f3 ~1 I. ?7 W
                        in.sigInst.WitnessComponents = []witnessComponent{}6 C4 \2 N8 S7 s7 G, l
                }( f7 W. O  W5 P5 T2 y1 o
                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)  r+ s1 o% r* ]7 U# T
                tx.Inputs = append(tx.Inputs, in.input). d! F; x) u0 [0 P9 J" _* D2 k! d9 z6 P
        }
( c- p% e. P4 I6 c        tpl.Transaction = types.NewTx(*tx)
& [/ M0 i) g9 x) m' R        return tpl, tx, nil
  R) T/ G! B3 w) e}, f2 K9 s5 A1 s/ }
6.对构造的交易进行签名
6 R" Q0 ~  O9 O: m账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)& |% L1 _& d( w) x; Z' n4 E
// Sign will try to sign all the witness/ `% G3 e/ h" E( T$ D/ N: e) v
func Sign(tpl *Template, xprv chainkd.XPrv) error {
' ^7 y7 q: m2 n5 E2 k) J        for i, sigInst := range tpl.SigningInstructions {: s; X! A) n* p' m% [
                h := tpl.Hash(uint32(i)).Byte32()
+ `* L6 o! C/ T' Z0 e3 x$ \                sig := xprv.Sign(h[:])
+ W8 h# N$ z0 k                rawTxSig := &RawTxSigWitness{$ t& L5 z8 ~2 ?# V; \
                        Quorum: 1,# ^7 l" f* q* e: r! C" G$ e- D
                        Sigs:   []json.HexBytes{sig},
8 y+ u) h0 ]" z' S1 I                }9 ?5 h$ x; ]# m: C& \; `
                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
7 I9 A/ C3 I4 \7 C2 ]: ?- `+ A7 q        }
6 |! z3 x& k9 v% ?        return materializeWitnesses(tpl)% F0 H- c: f+ j& S
}
5 m. Q$ H! _& b5 i' F7.提交交易上链
; _7 K3 ~' N  g# G8 ]! H" k该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可! l6 Z; o6 u$ j/ t  L' @
8 b5 V' ]' p  ]6 h% F+ Z/ }, w8 n
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16