Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

Bystack跨链技术源码揭秘

123458243
135 0 0
Bystack是由比原链团队提出的一主多侧链架构的BaaS平台。其将区块链应用分为三层架构:底层账本层,侧链扩展层,业务适配层。底层账本层为Layer1,即为目前比较成熟的采用POW共识的Bytom公链。侧链扩展层为Layer2,为多侧链层,vapor侧链即处于Layer2。
# x8 t5 A( N3 X1 q" C+ G0 Q6 A9 E( |9 R6 Q
Vapor侧链采用DPOS和BBFT共识,TPS可以达到数万。此处就分析一下连接Bytom主链和Vapor侧链的跨链模型。
1 S. Q6 G2 v( z  d) \# l主侧链协同工作模型5 S8 Z& f" ]+ M$ Y
3 Q$ u1 L; a+ I5 Y* L
1、技术细节
0 z/ Y) K+ n; h& TPOW当前因为能源浪费而饱受诟病,而且POW本身在提高TPS的过程中遇到诸多问题,理论上可以把块变大,可以往块里面塞更多的交易。TPS是每秒出块数*块里面的交易数。但是也存在问题:小节点吃不消存储这么大的容量的内容,会慢慢变成中心化的模式,因为只有大财团和大机构才有财力去组建机房设备,成为能出块的节点。同时传输也存在问题,网络带宽是有限的,块的大小与网络传输的边际是有关的,不可能无限的去增加块的大小,网络边际上的人拿不到新块的信息,也会降低去中心化的程度,这就是为什么POW不能在提高可靠性的情况下,提高TPS的原因。+ ^# k# U  z. C9 J
而BFT虽然去中心化较弱,但其效率和吞吐量高,也不需要大量的共识计算,非常环保节能,很符合Bystack侧链高TPS的性能需求) O3 I4 }+ c! m; V* @. q
(1)跨链模型架构
. A$ T! H/ t- I2 B在Bystack的主侧链协同工作模型中,包括有主链、侧链和Federation。主链为bytom,采用基于对AI 计算友好型PoW(工作量证明)算法,主要负责价值锚定,价值传输和可信存证。侧链为Vapor,采用DPOS+BBFT共识,高TPS满足垂直领域业务。主链和侧链之间的资产流通主要依靠Federation。
9 l: _  ^  K: D(2)节点类型
5 U1 C! `$ x  X$ @) h跨链模型中的节点主要有收集人、验证人和联邦成员。收集人监控联邦地址,收集交易后生成Claim交易进行跨链。验证人则是侧链的出块人。联邦成员由侧链的用户投票通过选举产生,负责生成新的联邦合约地址。
7 @, M* a% S. Z(3)跨链交易流程
- D" o. ?) t8 H! H0 e主链到侧链
  X$ d! B  L- R! U: l2 B主链用户将代币发送至联邦合约地址,收集人监控联邦地址,发现跨链交易后生成Claim交易,发送至侧链: x; Z7 s* o! y$ Y* }& a
侧链到主链
" S' M3 U( \' y3 o1 `侧链用户发起提现交易,销毁侧链资产。收集人监控侧链至主链交易,向主链地址发送对应数量资产。最后联邦在侧链生成一笔完成提现的操作交易。8 B1 L9 q, M9 ~4 q. k
2、代码解析4 ]  P& H  ]) O
跨链代码主要处于federation文件夹下,这里就这部分代码进行一个介绍。
1 u* ~0 P1 r. G(1)keeper启动
' I9 P% _+ J  g2 K; U3 U整个跨链的关键在于同步主链和侧链的区块,并处理区块中的跨链交易。这部份代码主要在mainchain_keerper.go和sidechain_keerper.go两部分中,分别对应处理主链和侧链的区块。keeper在Run函数中启动。' `  Z$ j% O- v8 `. ^7 q' \
func (m *mainchainKeeper) Run() {
1 S9 T& J" k! K1 R) |        ticker := time.NewTicker(time.Duration(m.cfg.SyncSeconds) * time.Second)
3 J$ h3 k; [5 L, [$ q/ ~        for ; true; , y% k: U% b0 G4 d  A% V) O" u
Run函数中首先生成一个定时的Ticker,规定每隔SyncSeconds秒同步一次区块,处理区块中的交易。) G! s3 ^: w1 @! s, h$ ]
(2)主侧链同步区块: j/ l' Z; e6 B
Run函数会调用syncBlock函数同步区块。) \& q9 W( B1 Z! n8 S
func (m *mainchainKeeper) syncBlock() (bool, error) {
+ ^/ \# ]7 K! _! L        chain := &orm.Chain{Name: m.chainName}. }& z/ j5 X) @- F" x/ C
        if err := m.db.Where(chain).First(chain).Error; err != nil {
" ?7 C8 c, J" ^, s0 m                return false, errors.Wrap(err, "query chain")
7 W/ ^  {; H: [& {        }
+ ~- `5 l7 W$ m& E4 x) ^        height, err := m.node.GetBlockCount(); o: E- D- I# W) c
        //..
+ z: w+ C& G* y# g  Z        if height 6 ?, ^) j. V' {6 \6 h- Q( l
这个函数受限会根据chainName从数据库中取出对应的chain。然后利用GetBlockCount函数获得chain的高度。然后进行一个伪确定性的检测。& b, v/ ~! f: z$ e
height + j  V5 K9 j4 y0 q
主要是为了判断链上的资产是否已经不可逆。这里Confirmations的值被设为10。如果不进行这个等待不可逆的过程,很可能主链资产跨链后,主链的最长链改变,导致这笔交易没有在主链被打包,而侧链却增加了相应的资产。在此之后,通过GetBlockByHeight函数获得chain的下一个区块。  V2 {, z# `; d, A: N1 g% _
nextBlockStr, txStatus, err := m.node.GetBlockByHeight(chain.BlockHeight + 1)3 s5 a1 q  W6 g$ p0 B; s. I
: \% D- R1 R4 l* u
这里必须满足下个区块的上一个区块哈希等于当前chain中的这个头部区块哈希。这也符合区块链的定义。( h& X$ C  B7 \4 L
if nextBlock.PreviousBlockHash.String() != chain.BlockHash {
- z  b" ]' B- @# P- q5 l  N    //..( Z& n; t. \' ~6 P  W5 |
}
* w$ Z/ \0 J- {( y1 o# ~' c在此之后,通过调用tryAttachBlock函数进一步调用processBlock函数处理区块。) T& M: p& O/ K
(3)区块处理( R- w* n0 u7 N6 h9 l( W; b, L: b
processBlock函数会判断区块中交易是否为跨链的deposit或者是withdraw,并分别调用对应的函数去进行处理。
. b9 L: ~# G0 `9 n- Efunc (m *mainchainKeeper) processBlock(chain *orm.Chain, block *types.Block, txStatus *bc.TransactionStatus) error {
& m! g& b9 y0 j8 S9 h        if err := m.processIssuing(block.Transactions); err != nil {7 e  q: T+ k: Q  V$ E
                return err
# @' s% h( P7 i4 `+ e7 Y        }8 C$ @8 V, Y: Z6 h
        for i, tx := range block.Transactions {# Q2 z0 r! ^% d; M) a
                if m.isDepositTx(tx) {
5 p5 H: F- Z, h' O7 e) h  e                        if err := m.processDepositTx(chain, block, txStatus, uint64(i), tx); err != nil {
9 o' c7 R8 e# f+ N/ d. P                                return err8 c1 u4 E1 w8 N
                        }
8 b8 {+ L& I; O* ~                }
: k- D" @+ r+ v3 u. m3 p/ A                if m.isWithdrawalTx(tx) {
+ ]2 w$ E( _$ p* p4 W8 g, a$ ^7 [                        if err := m.processWithdrawalTx(chain, block, uint64(i), tx); err != nil {5 E/ m( d. o# ~7 `
                                return err
) B3 F7 a; |- v: X                        }
1 n6 Z$ w/ f  t                }
* P- t9 J7 u6 a0 l  Y        }% N( E/ E8 ~3 g, `0 J4 O
        return m.processChainInfo(chain, block)
$ S( P/ ^& @4 _! m/ C. m}
, S0 {9 d- A, B: B2 W在这的processIssuing函数,它内部会遍历所有交易输入Input的资产类型,也就是AssetID。当这个AssetID不存在的时候,则会去在系统中创建一个对应的资产类型。每个Asset对应的数据结构如下所示。9 j* J8 y: b, X
m.assetStore.Add(&orm.Asset{
+ M2 t# g+ j- h. \AssetID:           assetID.String(),
9 v1 Y* p9 h  F' VIssuanceProgram:   hex.EncodeToString(inp.IssuanceProgram),
7 C! m* G. f2 o, j$ xVMVersion:         inp.VMVersion,% Z0 ~3 d# b+ P: ]' [. E# B6 L* P& `
RawDefinitionByte: hex.EncodeToString(inp.AssetDefinition),
; _3 ~* p; l) Y8 b' I9 M})" E2 E; a! R& a- e/ H4 ^
在processBlock函数中,还会判断区块中每笔交易是否为跨链交易。主要通过isDepositTx和isWithdrawalTx函数进行判断。
# p1 c( D  p6 ~* Y" d- f# Q) S: gfunc (m *mainchainKeeper) isDepositTx(tx *types.Tx) bool {; T+ Q3 n: a# D! [
        for _, output := range tx.Outputs {
: I. l4 q6 Y6 {% h3 u0 [, q                if bytes.Equal(output.OutputCommitment.ControlProgram, m.fedProg) {0 _/ y  `6 R$ x, u2 i' h6 W
                        return true; V  ^9 N) M+ L$ Z1 X$ y0 P. w
                }: }: g% H, x& f( m& C4 j0 A; O
        }* _) `5 V$ M9 X+ ~1 X5 Y4 q8 m
        return false) c3 \# I+ z. a6 M
}) Q. v2 o" _2 _% X4 Y/ \  ^, w
func (m *mainchainKeeper) isWithdrawalTx(tx *types.Tx) bool {
2 d  D' Z! |) k% ~  G        for _, input := range tx.Inputs {8 D* A" s" ^, I2 _+ t8 A
                if bytes.Equal(input.ControlProgram(), m.fedProg) {6 T7 B. H2 `3 l8 {" _$ R) J" q
                        return true2 G/ E( R( _# `, k$ @% e
                }
  v: A6 y$ g# v& X% ]- _        }
* L+ Y' W; \* _" b        return false  U" v- v& x/ I9 v
}1 T, o! n- M0 ]
看一下这两个函数,主要还是通过比较交易中的control program这个标识和mainchainKeeper这个结构体中的fedProg进行比较,如果相同则为跨链交易。fedProg在结构体中为一个字节数组。
% h+ y9 m# C4 ^; w  p  |type mainchainKeeper struct {
; G+ j1 r8 ?( d& k! N        cfg        *config.Chain
/ i1 d; i$ w$ t        db         *gorm.DB0 p9 O' w' J& n& k
        node       *service.Node
: N, h! c) u, [        chainName  string
+ }$ g, Z6 _% g1 B        assetStore *database.AssetStore6 R: R; R( r" p, V4 M. x
        fedProg    []byte
! R# d' J$ l0 T1 q7 c}9 S2 ~1 U6 q: F& V, m
(4)跨链交易(主链到侧链的deposit)处理
* r' z2 l; ^* ]( C& r这部分主要分为主链到侧链的deposit和侧链到主链的withdraw。先看比较复杂的主链到侧链的deposit这部分代码的处理。
8 j" Y( {# H% u" zfunc (m *mainchainKeeper) processDepositTx(chain *orm.Chain, block *types.Block, txStatus *bc.TransactionStatus, txIndex uint64, tx *types.Tx) error {
" I! X6 A! x3 v        //..& K3 z6 E2 H& d1 W* V$ Y6 S' h, s
        rawTx, err := tx.MarshalText()% _6 E( n& _9 ^! g
        if err != nil {
( N( s# _- R& |4 |$ o2 X4 [3 b8 U                return err
8 y  |# R' j3 d" ?6 d9 Q- Q: X        }# w9 P# V# C7 D& v' s
        ormTx := &orm.CrossTransaction{
% ]7 E/ ?3 R) w  U              //..
# i: P4 ?+ ?3 v( w6 M        }; b6 E& w' p9 v/ X7 }/ U
        if err := m.db.Create(ormTx).Error; err != nil {
: [. b- y' N; M2 A4 I' J/ Y% J7 C                return errors.Wrap(err, fmt.Sprintf("create mainchain DepositTx %s", tx.ID.String()))0 I  y& ?6 k, w3 M5 E
        }8 _; E* k0 r/ ~/ N
        statusFail := txStatus.VerifyStatus[txIndex].StatusFail
4 E( r9 I0 S* B7 }3 a: S        crossChainInputs, err := m.getCrossChainReqs(ormTx.ID, tx, statusFail)
( e' H% R$ a' z4 Z! w2 i        if err != nil {; W6 a" A& `- t. n7 ]
                return err
, P% t: e. k5 W+ s5 w, I' P1 O        }
" w2 E5 T$ l5 H! Z7 t( O        for _, input := range crossChainInputs {
4 L' S! J7 I' R                if err := m.db.Create(input).Error; err != nil {
3 S& A; Y' |& a8 i; w  o9 E  q                        return errors.Wrap(err, fmt.Sprintf("create DepositFromMainchain input: txid(%s), pos(%d)", tx.ID.String(), input.SourcePos))6 @% p) @- ^4 A$ K
                }6 ^# j8 F% e: ~
        }
- C0 R; z* @$ D        return nil
0 @* p2 ]( u8 m  G}
2 o3 F$ W. W7 Z6 T( w( h这里它创建了一个跨链交易orm。具体的结构如下。可以看到,这里它的结构体中包括有source和dest的字段。* R; B4 M: [/ N
ormTx := &orm.CrossTransaction{) b4 ^" C' L$ g
                ChainID:              chain.ID,* a4 Y. Q, Z% [2 f; }
                SourceBlockHeight:    block.Height,
7 P5 ]7 ?- B$ `                SourceBlockTimestamp: block.Timestamp,# `( h0 X+ |4 g# J; o. f
                SourceBlockHash:      blockHash.String(),
8 c1 k' U$ p1 u+ s                SourceTxIndex:        txIndex,# I- m  _( I6 Y; s* ?  e) W  B
                SourceMuxID:          muxID.String(),+ \& o* y2 C1 p/ [0 C' K& e! E, @
                SourceTxHash:         tx.ID.String(),1 J, Z% H" n; n4 G* z+ e5 y& I
                SourceRawTransaction: string(rawTx),+ @. W5 n4 Q3 s5 K# b
                DestBlockHeight:      sql.NullInt64{Valid: false},
' v- B& X) h! a: j% V0 s3 ?6 m                DestBlockTimestamp:   sql.NullInt64{Valid: false},5 z. M# Z; |. M
                DestBlockHash:        sql.NullString{Valid: false},
" t" X# z8 ~  m5 n6 ~2 N% Q0 h                DestTxIndex:          sql.NullInt64{Valid: false},
; o3 h# f2 D8 w( ^: O5 @                DestTxHash:           sql.NullString{Valid: false},2 g7 y( K' h) h
                Status:               common.CrossTxPendingStatus,
( X( z6 ?! @, ?1 f+ z        }
) Y3 \. p! ?/ B5 J创建这笔跨链交易后,它会将交易存入数据库中。/ l! q4 i6 J0 A2 Q, s
if err := m.db.Create(ormTx).Error; err != nil {
4 K: L; B% Y2 h. a( q. j2 E' \                return errors.Wrap(err, fmt.Sprintf("create mainchain DepositTx %s", tx.ID.String()))
4 x% s, X, T/ B3 v' l}
& X3 K! s0 o! \) M# G在此之后,这里会调用getCrossChainReqs。这个函数内部较为复杂,主要作用就是遍历交易的输出,返回一个跨链交易的请求数组。具体看下这个函数。. \* ~9 z+ _8 M. T: g
func (m *mainchainKeeper) getCrossChainReqs(crossTransactionID uint64, tx *types.Tx, statusFail bool) ([]*orm.CrossTransactionReq, error) {' O! l7 l& y- W& D+ M, N9 B" A
        //../ m: ]: O, _5 ?; ]
        switch {
- {* G2 q7 I+ i4 ~+ A- w, f4 p  h% Z% k        case segwit.IsP2WPKHScript(prog):; r" \! q, ]. L1 K  I5 B- ~/ G) H: Z# X
                //..  d" I: B  r! E: E( i5 s
        case segwit.IsP2WSHScript(prog):8 d  p) Y2 b0 M3 O: O. A
                //..
2 E$ w7 s" o+ B, p+ Z! N        }
( C* w- {0 \( P; H7 `3 `        reqs := []*orm.CrossTransactionReq{}3 U. Y6 w9 c5 n2 ?" D; v
        for i, rawOutput := range tx.Outputs {
5 ^( A# X6 i- \                //..
' M3 s9 x3 J  I: x$ A, @- t8 ?                req := &orm.CrossTransactionReq{8 I' Z; j# f( l# e0 ?0 x
                        //..
- ~& x6 [9 l. H9 X0 M$ g                }9 G- ~  Z# X& v1 Z% Y
                reqs = append(reqs, req)) o& z; n5 O9 b" Y6 H' m9 o) o
        }& W; T9 O$ u6 s
        return reqs, nil( K9 T" y8 b+ n
}" E7 l. E/ K0 w0 d, _: _
很显然,这个地方的交易类型有pay to public key hash 和 pay to script hash这两种。这里会根据不同的交易类型进行一个地址的获取。
2 i8 ?/ ~- h, {1 l  U1 R  `# Oswitch {+ k# T9 u. I* P% H! U8 }7 S
        case segwit.IsP2WPKHScript(prog):
  Z) G6 {; i+ v# s. f8 Z3 [                if pubHash, err := segwit.GetHashFromStandardProg(prog); err == nil {9 K3 E% b0 x4 f8 o
                        fromAddress = wallet.BuildP2PKHAddress(pubHash, &vaporConsensus.MainNetParams)
7 F0 v: X" ~6 l* @5 y5 u                        toAddress = wallet.BuildP2PKHAddress(pubHash, &vaporConsensus.VaporNetParams)0 @" {1 @2 B# ~& G; d
                }
. m9 @1 y$ I) X0 Y        case segwit.IsP2WSHScript(prog):
9 h, H9 y, e/ J) l* Y9 S                if scriptHash, err := segwit.GetHashFromStandardProg(prog); err == nil {8 K  T. Z) S) N5 p; D
                        fromAddress = wallet.BuildP2SHAddress(scriptHash, &vaporConsensus.MainNetParams)
) y3 r8 t  c! o5 O- f, F7 K% c                        toAddress = wallet.BuildP2SHAddress(scriptHash, &vaporConsensus.VaporNetParams)
2 }3 W' S3 y, m5 m% j$ t. n/ U                }
) i! v. H1 P) i        }
9 r9 z( @) e. \. X$ G' S- j在此之后,函数会遍历所有交易的输出,然后创建跨链交易请求,具体的结构如下。
& N- A; F$ s7 U# y* s4 I, X8 O: Hreq := &orm.CrossTransactionReq{
1 H; G4 L8 ]4 P- U$ e   CrossTransactionID: crossTransactionID,
. b) b$ u2 X# C2 M   SourcePos:          uint64(i),. n! n# A7 K" |* R2 R
   AssetID:            asset.ID,
1 z' k% D# r& z5 q( g4 b0 g2 b& b   AssetAmount:        rawOutput.OutputCommitment.AssetAmount.Amount,
% c0 I! L: x" b9 v' w/ o+ b& t   Script:             script,2 @: _6 V0 q! \% k7 ~
   FromAddress:        fromAddress,
1 o7 r' f3 p; Z) A   ToAddress:          toAddress,
% t! Y' ~# X6 n+ {   }& z+ f  `1 U$ {7 `/ M
创建完所有的跨链交易请求后,返回到processDepositTx中一个crossChainInputs数组中,并存入db。
" c- U: L& k; s/ i) J5 s& c: Dfor _, input := range crossChainInputs {
( U* V, b! [6 V4 |% d+ B                if err := m.db.Create(input).Error; err != nil {
' d! I5 ^! M. d7 q2 }: P  x                        return errors.Wrap(err, fmt.Sprintf("create DepositFromMainchain input: txid(%s), pos(%d)", tx.ID.String(), input.SourcePos))$ V8 E6 ^0 U7 T- p
                }
& a) j: L4 N+ D$ H/ {0 N}
! f7 l3 U! K; W* W2 a到这里,对主链到侧链的deposit已经处理完毕。- T7 X6 ]4 ~5 j& Q% M. `* k
(5)跨链交易(侧链到主链的withdraw)交易处理
- S, G3 E. O. f. t" ~4 V这部分比较复杂的逻辑主要在sidechain_keeper.go中的processWithdrawalTx函数中。这部分逻辑和上面主链到侧链的deposit逻辑类似。同样是创建了orm.crossTransaction结构体,唯一的改变就是交易的souce和dest相反。这里就不作具体描述了。
# x1 \# \- D1 w0 M3、跨链优缺点8 j4 J! `7 L) U8 H5 _) |5 L
优点
$ ?- v7 R) Q3 j: \" ^$ \7 j2 ?' X5 J1 S(1) 跨链模型、代码较为完整。当前有很多项目使用跨链技术,但是真正实现跨链的寥寥无几。: i/ h+ \3 G, I. r# r$ A
(2) 可以根据不同需求实现侧链,满足多种场景
4 e( y/ E; c; [, K0 `缺点
* c8 n; N& y, Z(1) 跨链速度较慢,需等待10个区块确认,这在目前Bytom网络上所需时间为30分钟左右- I: v  f6 K8 Z, [1 P
(2) 相较于comos、polkadot等项目,开发者要开发侧链接入主网成本较大
, B7 _- t7 P: e) s8 A(3) 只支持资产跨链,不支持跨链智能合约调用5 O/ E, F/ h* B. l& h
4、跨链模型平行对比Cosmos5 Q$ L4 U, u$ @" I" Y* z2 W
可扩展性
" j: m9 O' s( s0 T2 N; B' rbystack的主测链协同工作模型依靠Federation,未形成通用协议。其他开发者想要接入其跨链网络难度较大。Cosmos采用ibc协议,可扩展性较强。
7 K8 ]3 k' S! d# G2 Q3 p代码开发进度
2 \7 W! W( h# b( pvapor侧链已经能够实现跨链。Cosmos目前暂无成熟跨链项目出现,ibc协议处于最终开发阶段。
5 j7 V% b' q+ ~9 a- _, s跨链模型, m$ T. Y# v; G6 s4 {! ?9 E$ D
vapor为主侧链模型,Cosmos为Hub-Zone的中继链模型。3 Q. I: a  g1 O" t* U+ I
5、参考建议7 O' p8 b7 X6 Y; }$ p( h& _* K
侧链使用bbft共识,非POW的情况下,无需等待10个交易确认,增快跨链速度。
标签: Bystack 跨链
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

123458243 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    1