Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文
比原项目仓库:3 s) Q4 j% N2 s4 ~0 R, P/ a9 h
Github地址:https://github.com/Bytom/bytom
- T' ^: I0 E9 b; T: p! ~4 HGitee地址:https://gitee.com/BytomBlockchain/bytom
. v4 R. N4 `  H, V0 x该部分主要针对用户自己管理私钥和地址,并通过utxo来构建和发送交易。+ W. C1 z$ _' I! g, `, t# K
1.创建私钥和公钥2.根据公钥创建接收对象3.找到可花费的utxo4.通过utxo构造交易5.组合交易的input和output构成交易模板6.对构造的交易进行签名7.提交交易上链2 j# k7 `! o0 s5 Y5 L4 k
$ Y+ Q! U" H# W$ h! K/ B& Z" V. i3 D
注意事项:3 z+ I; U- S) _$ l- O
以下步骤以及功能改造仅供参考,具体代码实现需要用户根据实际情况进行调试,具体可以参考单元测试案例代码blockchain/txbuilder/txbuilder_test.go#L255
% z2 F/ V1 t# v, A" x& P6 y4 M1 E) d6 D1.创建私钥和公钥' W2 s3 H  `9 k- d9 z, M0 \" j
该部分功能可以参考代码crypto/ed25519/chainkd/util.go#L11,可以通过 NewXKeys(nil) 创建主私钥和主公钥4 n) k4 d6 ~# s- v# O! B
func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {0 G8 H( e1 T8 V( O8 {* k
        xprv, err = NewXPrv(r)
! n# Q7 ]/ U$ V$ x( p7 h        if err != nil {
& D/ a& G3 z( }* p                return% H: N7 S, C8 b6 v1 O
        }) Y- z& b8 @" h! f! X( V% O
        return xprv, xprv.XPub(), nil
" H& v7 s. c4 t4 G: d9 W& a}
! D6 y% ~6 o& Y& K/ b2.根据公钥创建接收对象  D2 J) M! F& O$ O
接收对象包含两种形式:address形式和program形式,两者是一一对应的,任选其一即可。其中创建单签地址参考代码account/accounts.go#L267进行相应改造为:6 a( L: n8 W  k+ g; Q
func (m *Manager) createP2PKH(xpub chainkd.XPub) (*CtrlProgram, error) {9 @9 L  _/ Z4 x
        pubKey := xpub.PublicKey()
, B6 Q: g$ {/ x/ V% |5 k$ H) s- ]8 Q5 Y        pubHash := crypto.Ripemd160(pubKey)
$ N- J- h2 h# P3 S( s$ F; S* W        // TODO: pass different params due to config3 P# _1 z. g9 ~0 X
        address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
1 T& _" x3 C4 Q# J5 P        if err != nil {
% N$ `; ?, o; y2 E+ Y# y                return nil, err  [% f- o' h4 ]5 a; z- f) E
        }) D% s2 _3 k& \  X- I  M
        control, err := vmutil.P2WPKHProgram([]byte(pubHash))" U* W1 Z3 }5 f7 u
        if err != nil {- v; z7 D2 Q9 X4 [% [2 D
                return nil, err
+ k  h6 {" y3 c, y3 k- ~0 V        }3 H: c- [# l  q9 A; u: q
        return &CtrlProgram{
) _7 N# ]' X7 P6 L                Address:        address.EncodeAddress(),% K! \- x. i+ H8 O
                ControlProgram: control,: T' ]7 P) K# R# b( m$ k
        }, nil
% l* {) z: b; D8 i) ^2 ~' {9 k0 f}% O; L5 a  o: V- x& c7 X9 ~
创建多签地址参考代码account/accounts.go#L294进行相应改造为:
; d% k8 B8 E& K9 i, g' t  [func (m *Manager) createP2SH(xpubs []chainkd.XPub) (*CtrlProgram, error) {
0 E- c* `7 Z& p+ T1 x3 u/ N        derivedPKs := chainkd.XPubKeys(xpubs)" [0 T) _$ r* H0 o
        signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
4 f) j2 e5 \2 |# e) o0 t        if err != nil {
5 Z3 z" E" s7 ^                return nil, err( ?. F8 P8 M  l5 x0 _3 p
        }6 J7 ^8 B! E9 B/ R5 g. X
        scriptHash := crypto.Sha256(signScript)9 Q6 s. \2 |/ G+ h8 a* Y' O# t
        // TODO: pass different params due to config
2 m9 K+ [+ Y" p  k" [        address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams). X$ g3 y5 l1 N. D+ u9 t: i
        if err != nil {: O/ _% w% L4 H
                return nil, err6 N" S) j& T9 V8 ]5 y5 ^  s
        }1 y0 A1 ?- J# |2 Z$ D" g
        control, err := vmutil.P2WSHProgram(scriptHash)
  P# G: [7 R  J2 B8 X        if err != nil {
# t# ?) K  F9 z2 H$ Z                return nil, err
9 E: M* r0 m8 Y) ^0 @' I        }
- g2 u4 Y7 S1 Q+ x! }0 D  s        return &CtrlProgram{* Q) t! z& J' z& s
                Address:        address.EncodeAddress(),, s' |* d) l+ y! [' S, E
                ControlProgram: control,
: B: w4 R" w+ P        }, nil
2 d- A/ B1 G* I4 [8 c}# _0 O9 ^! Q( L: y- G
3.找到可花费的utxo
) Z/ b( _8 c) c+ w1 M找到可花费的utxo,其实就是找到接收地址或接收program是你自己的unspend_output。其中utxo的结构为:(参考代码account/reserve.go#L39)0 V- v0 c+ X1 z; ~3 E
// UTXO describes an individual account utxo.6 ]' Z. ~( a0 O# X+ \
type UTXO struct {
) X% N3 G6 C/ r% R# F, \        OutputID bc.Hash! z7 I/ w( e. `7 k2 Y/ w
        SourceID bc.Hash# K; @5 F5 X- U  o
        // Avoiding AssetAmount here so that new(utxo) doesn't produce an2 G; j$ k) H# n! [- u: Y2 F
        // AssetAmount with a nil AssetId.0 i$ l, H+ P" E% H* K3 E" x
        AssetID bc.AssetID
+ S4 y( ?' ]& t        Amount  uint64' V0 {$ n* p7 b, v/ v9 `$ ]! {
        SourcePos      uint64
& v' P! c9 H8 @' x& @( v8 e  \) F        ControlProgram []byte3 `* i" ?3 P' O& i3 c
        AccountID           string
1 H& [. s7 t: Y% S! e) B* E# u% l8 n        Address             string  k- B7 [% ?' @" L# _
        ControlProgramIndex uint64
, O7 c# }5 Q% s7 ?" e; Y6 K' W2 {        ValidHeight         uint64* Q; Z# c1 F& y
        Change              bool9 c( t, _. V* \0 B, Z' n
}
# ^/ t1 B6 V! B8 e/ Z7 k涉及utxo构造交易的相关字段说明如下:% i! ?9 x2 Q$ V  N( O# I  {
SourceID 前一笔关联交易的mux_id, 根据该ID可以定位到前一笔交易的outputAssetID utxo的资产IDAmount utxo的资产数目SourcePos 该utxo在前一笔交易的output的位置ControlProgram utxo的接收programAddress utxo的接收地址$ u/ P$ m, O7 t! m0 S) A
( F% p$ s* p* {. Y% ]
上述这些utxo的字段信息可以从get-block接口返回结果的transaction中找到,其相关的结构体如下:(参考代码api/block_retrieve.go#L26)1 j& O5 V( K" \2 t
// BlockTx is the tx struct for getBlock func
6 p/ s- ~, b; W; W5 Etype BlockTx struct {  X* Y3 H0 u9 Y
        ID         bc.Hash                  `json:"id"`
  o5 D0 P9 t0 N) w& e3 Q5 v        Version    uint64                   `json:"version"`9 e  P% c3 b7 j' b
        Size       uint64                   `json:"size"`, ?% q# ]6 E" B) G! J
        TimeRange  uint64                   `json:"time_range"`
; d$ o( p2 @9 t; k/ P7 R6 H  z6 U6 Y        Inputs     []*query.AnnotatedInput  `json:"inputs"`5 q* w, R  q4 ?7 R
        Outputs    []*query.AnnotatedOutput `json:"outputs"`2 W' O; d8 H2 c, z& P
        StatusFail bool                     `json:"status_fail"`+ I1 x- u& ^# `* N0 Z
        MuxID      bc.Hash                  `json:"mux_id"`: r- @; B5 c. b- b+ i% |6 _
}$ v0 X( O; k! q$ H6 _# l1 M" S
//AnnotatedOutput means an annotated transaction output.
% H- l# S% r! Z) M; qtype AnnotatedOutput struct {
' _) p3 f( \7 n) |- k! [5 ], i5 ^        Type            string             `json:"type"`4 p/ w# Q) p1 S) K/ u
        OutputID        bc.Hash            `json:"id"`3 _0 f5 ^& b# \  A* g0 [9 W( F; x
        TransactionID   *bc.Hash           `json:"transaction_id,omitempty"`
: w' h* z, ~+ f* ~, ^        Position        int                `json:"position"`# l  ~" r8 v* Z$ p1 x
        AssetID         bc.AssetID         `json:"asset_id"`, z; E& ~7 G! O. Y: U9 q
        AssetAlias      string             `json:"asset_alias,omitempty"`" _8 V; ^/ A% Y/ b8 B
        AssetDefinition *json.RawMessage   `json:"asset_definition,omitempty"`
+ ]" `, e/ \% G" B7 X7 l) L        Amount          uint64             `json:"amount"`
) Y) e' n% V) |8 _        AccountID       string             `json:"account_id,omitempty"`
/ j9 M* E: C; @  {4 ~        AccountAlias    string             `json:"account_alias,omitempty"`( Q+ l7 |. z+ s3 p
        ControlProgram  chainjson.HexBytes `json:"control_program"`- \7 d- X6 C: N% E( T' h9 v+ m
        Address         string             `json:"address,omitempty"`: {# m3 j. R4 C1 X% Y
}7 N2 L, Y' w+ h& u: ~
utxo跟get-block返回结果的字段对应关系如下:0 ]0 T% I# ?4 c/ I. d  v
`SourceID`       - `json:"mux_id"`
: |2 d4 M* E" q% I! s: S`AssetID`        - `json:"asset_id"`1 x' r: p1 c( O/ d8 D
`Amount`         - `json:"amount"`
. _/ ^/ v& r: X7 ?`SourcePos`      - `json:"position"`
% i6 n1 t( C- \2 H. J`ControlProgram` - `json:"control_program"`+ d6 |% m3 z$ s) c) k0 D
`Address`        - `json:"address,omitempty"`
' o9 T" w  y" W: j0 a/ m* T4.通过utxo构造交易
/ V; C1 k6 Q0 H通过utxo构造交易就是使用spend_account_unspent_output的方式来花费指定的utxo。) I  r; U6 d. _
第一步,通过utxo构造交易输入TxInput和签名需要的数据信息SigningInstruction,该部分功能可以参考代码account/builder.go#L169进行相应改造为:) c) q# V2 d* ~9 S' ]4 a8 t% D
// UtxoToInputs convert an utxo to the txinput' t" ^$ [6 u2 j6 J, ~
func UtxoToInputs(xpubs []chainkd.XPub, u *UTXO) (*types.TxInput, *txbuilder.SigningInstruction, error) {) J& V- S2 f# X1 L( o& K& H
        txInput := types.NewSpendInput(nil, u.SourceID, u.AssetID, u.Amount, u.SourcePos, u.ControlProgram)1 A! @) E; h9 B5 q5 r- R- `. k" o. c
        sigInst := &txbuilder.SigningInstruction{}
" p/ |! z& o( F        if u.Address == "" {
; J+ S3 F! G5 }- n( b5 D' S4 W                return txInput, sigInst, nil! L- u3 s3 V; g; V
        }9 s$ y) ~2 |( M! W
        address, err := common.DecodeAddress(u.Address, &consensus.ActiveNetParams)# n$ _- l3 C" U( f
        if err != nil {  Z2 b1 i9 d+ q8 I; R
                return nil, nil, err. n4 ^6 c6 @/ M# F4 Z. E: O) M% |& E, ~
        }7 Q3 Z) v  o4 T/ `- U) X$ Y
        switch address.(type) {
5 p* D+ M+ ?6 d% C8 v1 _6 |        case *common.AddressWitnessPubKeyHash:
* k# M1 Q( m7 S0 d/ R# {3 W$ @                derivedPK := xpubs[0].PublicKey()
0 k, E- U8 S: k+ Q9 q) t) Q                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness([]byte(derivedPK)))! u8 `& b' n( U/ Y$ L
        case *common.AddressWitnessScriptHash:
2 q8 ^1 {  {1 {# Y3 K2 ]  [                derivedPKs := chainkd.XPubKeys(xpubs)" ]+ D$ r, j6 O2 Z! g% z
                script, err := vmutil.P2SPMultiSigProgram(derivedPKs, len(derivedPKs))
8 d: W0 p! f' S                if err != nil {
1 W+ t$ j9 o9 u                        return nil, nil, err; D& y: H1 A: K$ b
                }# v' ~: m/ B# _: T
                sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(script))% I" \* A1 G* s5 h0 a
        default:
( w3 k" j# Y; Z1 r0 ~* N  |                return nil, nil, errors.New("unsupport address type")
7 j9 j0 X5 D3 X* P  ^5 `( ^        }
# l; e! n( G4 F8 b0 g: h7 M        return txInput, sigInst, nil# R) T2 P* e$ z  q7 J
}& s  p8 f% v0 o" r
第二步,通过utxo构造交易输出TxOutput; ]" Q* f, w3 E; N
该部分功能可以参考代码protocol/bc/types/txoutput.go#L20:
1 _- K4 U& F. r9 n// NewTxOutput create a new output struct7 j% O) \, g% Z
func NewTxOutput(assetID bc.AssetID, amount uint64, controlProgram []byte) *TxOutput {
$ Y: g4 M5 P+ j( P        return &TxOutput{, U1 {( }7 _! L" U
                AssetVersion: 1," T. Y8 w5 t7 q3 {
                OutputCommitment: OutputCommitment{
: X* Y/ B( {7 J. {6 D( o$ M% D                        AssetAmount: bc.AssetAmount{
% A4 c3 L; v4 v" T                                AssetId: &assetID,: I2 |  J; u5 A( G. ^# m
                                Amount:  amount,
( F) q) G2 O6 [& K# y                        },
7 s4 t0 K4 f6 |' T* B1 p                        VMVersion:      1,
4 l6 O( y, m+ b( V7 @0 T1 w                        ControlProgram: controlProgram,& A2 K1 @' P1 k0 u/ ~4 a% k4 n% g3 j
                },
" f- F- Z9 u2 ^$ p) `* Y  O        }( @; x) l* ?9 g. p( B! |
}9 V" E& j. R7 r% B
5.组合交易的input和output构成交易模板
5 o7 i5 N7 N! }; D通过上面已经生成的交易信息构造交易txbuilder.Template,该部分功能可以参考blockchain/txbuilder/builder.go#L92进行改造为:9 f3 {+ e2 b: ]
type InputAndSigInst struct {  u, K9 L2 g# `; n/ O) L" E
        input *types.TxInput
1 N7 M- A8 P3 q9 E% R( W& C/ S* [        sigInst *SigningInstruction/ K7 s+ z( p& U- F. N" z/ [4 v
}& P: c# j7 d' B7 V0 Y
// Build build transactions with template
+ f( i2 C3 |6 ]; V8 W3 Ofunc BuildTx(inputs []InputAndSigInst, outputs []*types.TxOutput) (*Template, *types.TxData, error) {4 z' B+ N4 U9 l! b6 e, W
        tpl := &Template{}. \7 m, t9 T- j5 m8 p; _
        tx := &types.TxData{}
" ~/ L# U2 A3 c* G# U' s" Q        // Add all the built outputs., M; t- x9 S$ k. `, C5 B$ J9 a
        tx.Outputs = append(tx.Outputs, outputs...)% `  Q) M" p* p# x9 h4 i4 o
        // Add all the built inputs and their corresponding signing instructions.0 O4 ~# ~0 }' X3 M
        for _, in := range inputs {) B- z. G% E6 n5 R  X8 ^# x0 X8 o
                // Empty signature arrays should be serialized as empty arrays, not null.& v7 F! q1 U$ l7 P$ H
                in.sigInst.Position = uint32(len(inputs))4 e$ e0 x6 l/ k- J& h# {" m  ]
                if in.sigInst.WitnessComponents == nil {
. s* U; Q, Z3 e! W7 e, }3 g                        in.sigInst.WitnessComponents = []witnessComponent{}
2 m6 [, Z9 F- z* ^8 S                }- B* j1 }* K; Q7 g$ B- h' a) \
                tpl.SigningInstructions = append(tpl.SigningInstructions, in.sigInst)
. Y9 ^% E8 @( ^" J+ _$ [* ~! \                tx.Inputs = append(tx.Inputs, in.input)
* T. ^. E  y# _/ Q* Z- |! R        }$ J8 v2 L' l; j+ {3 }. C
        tpl.Transaction = types.NewTx(*tx)
/ u( A; Q- P  n, K        return tpl, tx, nil
+ p7 r  v1 `* W7 i}
  o: ?/ B$ n: O  `* Z6.对构造的交易进行签名
) n' N4 H2 a( j3 U3 \) Y账户模型是根据密码找到对应的私钥对交易进行签名,这里用户可以直接使用私钥对交易进行签名,可以参考签名代码blockchain/txbuilder/txbuilder.go#L82进行改造为:(以下改造仅支持单签交易,多签交易用户可以参照该示例进行改造)" w' M( j6 O/ Y3 r- ~0 ^' c% N+ t
// Sign will try to sign all the witness  u2 e/ j+ |$ u' J+ V
func Sign(tpl *Template, xprv chainkd.XPrv) error {% z: V3 R1 O, q3 Z1 W
        for i, sigInst := range tpl.SigningInstructions {
6 Z$ k4 G6 w- t. l$ Y; L: S5 P                h := tpl.Hash(uint32(i)).Byte32()
) Q" N  I7 v" u# W                sig := xprv.Sign(h[:])7 d* k: `2 Z0 ^. f% y: J6 X! T- M
                rawTxSig := &RawTxSigWitness{; c1 Y$ e: ~6 H7 A
                        Quorum: 1,
) i" u( R( W) c3 p- ?' v9 [# P. x( a& ^                        Sigs:   []json.HexBytes{sig},
1 e) D- H  t3 x8 k. X                }
+ c9 i  T5 L; y! k' ~9 d                sigInst.WitnessComponents = append([]witnessComponent(rawTxSig), sigInst.WitnessComponents...)
: v+ k$ N" K) X; c        }- _* _4 @3 A4 p9 K) x
        return materializeWitnesses(tpl)
# q. I/ p7 w& H}
' V: G' ^7 G% g9 t% |* c- d! b7.提交交易上链
: }. M2 e& [. R; ^; p; }1 G该步骤无需更改任何内容,直接参照wiki中提交交易的APIsubmit-transaction的功能即可
( s8 _7 l! q$ R; Q8 E0 f$ s9 z0 [' z
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

朋友一起走 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    16