Bystack是由比原链团队提出的一主多侧链架构的BaaS平台。其将区块链应用分为三层架构:底层帐本层,侧链扩展层,业务适配层。底层帐本层为Layer1,即为目前比较成熟的采用POW共识的Bytom公链。侧链扩展层为Layer2,为多侧链层,vapor侧链即处于Layer2。node
(图片来自Bystack白皮书)算法
Vapor侧链采用DPOS和BBFT共识,TPS能够达到数万。此处就分析一下链接Bytom主链和Vapor侧链的跨链模型。sql
主侧链协同工做模型数据库
一、技术细节数组
POW当前由于能源浪费而饱受诟病,并且POW自己在提升TPS的过程当中遇到诸多问题,理论上能够把块变大,能够往块里面塞更多的交易。TPS是每秒出块数*块里面的交易数。可是也存在问题:小节点吃不消存储这么大的容量的内容,会慢慢变成中心化的模式,由于只有大财团和大机构才有财力去组建机房设备,成为能出块的节点。同时传输也存在问题,网络带宽是有限的,块的大小与网络传输的边际是有关的,不可能无限的去增长块的大小,网络边际上的人拿不到新块的信息,也会下降去中心化的程度,这就是为何POW不能在提升可靠性的状况下,提升TPS的缘由。网络
而BFT虽然去中心化较弱,但其效率和吞吐量高,也不须要大量的共识计算,很是环保节能,很符合Bystack侧链高TPS的性能需求数据结构
(1)跨链模型架构架构
在Bystack的主侧链协同工做模型中,包括有主链、侧链和Federation。主链为bytom,采用基于对AI 计算友好型PoW(工做量证实)算法,主要负责价值锚定,价值传输和可信存证。侧链为Vapor,采用DPOS+BBFT共识,高TPS知足垂直领域业务。主链和侧链之间的资产流通主要依靠Federation。app
(2)节点类型ide
跨链模型中的节点主要有收集人、验证人和联邦成员。收集人监控联邦地址,收集交易后生成Claim交易进行跨链。验证人则是侧链的出块人。联邦成员由侧链的用户投票经过选举产生,负责生成新的联邦合约地址。
(3)跨链交易流程
主链到侧链
主链用户将代币发送至联邦合约地址,收集人监控联邦地址,发现跨链交易后生成Claim交易,发送至侧链
侧链到主链
侧链用户发起提现交易,销毁侧链资产。收集人监控侧链至主链交易,向主链地址发送对应数量资产。最后联邦在侧链生成一笔完成提现的操做交易。
二、代码解析
跨链代码主要处于federation文件夹下,这里就这部分代码进行一个介绍。
(1)keeper启动
整个跨链的关键在于同步主链和侧链的区块,并处理区块中的跨链交易。这部份代码主要在mainchain_keerper.go和sidechain_keerper.go两部分中,分别对应处理主链和侧链的区块。keeper在Run函数中启动。
func (m *mainchainKeeper) Run() { ticker := time.NewTicker(time.Duration(m.cfg.SyncSeconds) * time.Second) for ; true; <-ticker.C { for { isUpdate, err := m.syncBlock() if err != nil { //.. } if !isUpdate { break } } } }
Run函数中首先生成一个定时的Ticker,规定每隔SyncSeconds秒同步一次区块,处理区块中的交易。
(2)主侧链同步区块
Run函数会调用syncBlock函数同步区块。
func (m *mainchainKeeper) syncBlock() (bool, error) { chain := &orm.Chain{Name: m.chainName} if err := m.db.Where(chain).First(chain).Error; err != nil { return false, errors.Wrap(err, "query chain") } height, err := m.node.GetBlockCount() //.. if height <= chain.BlockHeight+m.cfg.Confirmations { return false, nil } nextBlockStr, txStatus, err := m.node.GetBlockByHeight(chain.BlockHeight + 1) //.. nextBlock := &types.Block{} if err := nextBlock.UnmarshalText([]byte(nextBlockStr)); err != nil { return false, errors.New("Unmarshal nextBlock") } if nextBlock.PreviousBlockHash.String() != chain.BlockHash { //... return false, ErrInconsistentDB } if err := m.tryAttachBlock(chain, nextBlock, txStatus); err != nil { return false, err } return true, nil }
这个函数受限会根据chainName从数据库中取出对应的chain。而后利用GetBlockCount函数得到chain的高度。而后进行一个伪肯定性的检测。
height <= chain.BlockHeight+m.cfg.Confirmations
主要是为了判断链上的资产是否已经不可逆。这里Confirmations的值被设为10。若是不进行这个等待不可逆的过程,极可能主链资产跨链后,主链的最长链改变,致使这笔交易没有在主链被打包,而侧链却增长了相应的资产。在此以后,经过GetBlockByHeight函数得到chain的下一个区块。
nextBlockStr, txStatus, err := m.node.GetBlockByHeight(chain.BlockHeight + 1)
这里必须知足下个区块的上一个区块哈希等于当前chain中的这个头部区块哈希。这也符合区块链的定义。
if nextBlock.PreviousBlockHash.String() != chain.BlockHash { //.. }
在此以后,经过调用tryAttachBlock函数进一步调用processBlock函数处理区块。
(3)区块处理
processBlock函数会判断区块中交易是否为跨链的deposit或者是withdraw,并分别调用对应的函数去进行处理。
func (m *mainchainKeeper) processBlock(chain *orm.Chain, block *types.Block, txStatus *bc.TransactionStatus) error { if err := m.processIssuing(block.Transactions); err != nil { return err } for i, tx := range block.Transactions { if m.isDepositTx(tx) { if err := m.processDepositTx(chain, block, txStatus, uint64(i), tx); err != nil { return err } } if m.isWithdrawalTx(tx) { if err := m.processWithdrawalTx(chain, block, uint64(i), tx); err != nil { return err } } } return m.processChainInfo(chain, block) }
在这的processIssuing函数,它内部会遍历全部交易输入Input的资产类型,也就是AssetID。当这个AssetID不存在的时候,则会去在系统中建立一个对应的资产类型。每一个Asset对应的数据结构以下所示。
m.assetStore.Add(&orm.Asset{ AssetID: assetID.String(), IssuanceProgram: hex.EncodeToString(inp.IssuanceProgram), VMVersion: inp.VMVersion, RawDefinitionByte: hex.EncodeToString(inp.AssetDefinition), })
在processBlock函数中,还会判断区块中每笔交易是否为跨链交易。主要经过isDepositTx和isWithdrawalTx函数进行判断。
func (m *mainchainKeeper) isDepositTx(tx *types.Tx) bool { for _, output := range tx.Outputs { if bytes.Equal(output.OutputCommitment.ControlProgram, m.fedProg) { return true } } return false } func (m *mainchainKeeper) isWithdrawalTx(tx *types.Tx) bool { for _, input := range tx.Inputs { if bytes.Equal(input.ControlProgram(), m.fedProg) { return true } } return false }
看一下这两个函数,主要仍是经过比较交易中的control program这个标识和mainchainKeeper这个结构体中的fedProg进行比较,若是相同则为跨链交易。fedProg在结构体中为一个字节数组。
type mainchainKeeper struct { cfg *config.Chain db *gorm.DB node *service.Node chainName string assetStore *database.AssetStore fedProg []byte }
(4)跨链交易(主链到侧链的deposit)处理
这部分主要分为主链到侧链的deposit和侧链到主链的withdraw。先看比较复杂的主链到侧链的deposit这部分代码的处理。
func (m *mainchainKeeper) processDepositTx(chain *orm.Chain, block *types.Block, txStatus *bc.TransactionStatus, txIndex uint64, tx *types.Tx) error { //.. rawTx, err := tx.MarshalText() if err != nil { return err } ormTx := &orm.CrossTransaction{ //.. } if err := m.db.Create(ormTx).Error; err != nil { return errors.Wrap(err, fmt.Sprintf("create mainchain DepositTx %s", tx.ID.String())) } statusFail := txStatus.VerifyStatus[txIndex].StatusFail crossChainInputs, err := m.getCrossChainReqs(ormTx.ID, tx, statusFail) if err != nil { return err } for _, input := range crossChainInputs { if err := m.db.Create(input).Error; err != nil { return errors.Wrap(err, fmt.Sprintf("create DepositFromMainchain input: txid(%s), pos(%d)", tx.ID.String(), input.SourcePos)) } } return nil }
这里它建立了一个跨链交易orm。具体的结构以下。能够看到,这里它的结构体中包括有source和dest的字段。
ormTx := &orm.CrossTransaction{ ChainID: chain.ID, SourceBlockHeight: block.Height, SourceBlockTimestamp: block.Timestamp, SourceBlockHash: blockHash.String(), SourceTxIndex: txIndex, SourceMuxID: muxID.String(), SourceTxHash: tx.ID.String(), SourceRawTransaction: string(rawTx), DestBlockHeight: sql.NullInt64{Valid: false}, DestBlockTimestamp: sql.NullInt64{Valid: false}, DestBlockHash: sql.NullString{Valid: false}, DestTxIndex: sql.NullInt64{Valid: false}, DestTxHash: sql.NullString{Valid: false}, Status: common.CrossTxPendingStatus, }
建立这笔跨链交易后,它会将交易存入数据库中。
if err := m.db.Create(ormTx).Error; err != nil { return errors.Wrap(err, fmt.Sprintf("create mainchain DepositTx %s", tx.ID.String())) }
在此以后,这里会调用getCrossChainReqs。这个函数内部较为复杂,主要做用就是遍历交易的输出,返回一个跨链交易的请求数组。具体看下这个函数。
func (m *mainchainKeeper) getCrossChainReqs(crossTransactionID uint64, tx *types.Tx, statusFail bool) ([]*orm.CrossTransactionReq, error) { //.. switch { case segwit.IsP2WPKHScript(prog): //.. case segwit.IsP2WSHScript(prog): //.. } reqs := []*orm.CrossTransactionReq{} for i, rawOutput := range tx.Outputs { //.. req := &orm.CrossTransactionReq{ //.. } reqs = append(reqs, req) } return reqs, nil }
很显然,这个地方的交易类型有pay to public key hash 和 pay to script hash这两种。这里会根据不一样的交易类型进行一个地址的获取。
switch { case segwit.IsP2WPKHScript(prog): if pubHash, err := segwit.GetHashFromStandardProg(prog); err == nil { fromAddress = wallet.BuildP2PKHAddress(pubHash, &vaporConsensus.MainNetParams) toAddress = wallet.BuildP2PKHAddress(pubHash, &vaporConsensus.VaporNetParams) } case segwit.IsP2WSHScript(prog): if scriptHash, err := segwit.GetHashFromStandardProg(prog); err == nil { fromAddress = wallet.BuildP2SHAddress(scriptHash, &vaporConsensus.MainNetParams) toAddress = wallet.BuildP2SHAddress(scriptHash, &vaporConsensus.VaporNetParams) } }
在此以后,函数会遍历全部交易的输出,而后建立跨链交易请求,具体的结构以下。
req := &orm.CrossTransactionReq{ CrossTransactionID: crossTransactionID, SourcePos: uint64(i), AssetID: asset.ID, AssetAmount: rawOutput.OutputCommitment.AssetAmount.Amount, Script: script, FromAddress: fromAddress, ToAddress: toAddress, }
建立完全部的跨链交易请求后,返回到processDepositTx中一个crossChainInputs数组中,并存入db。
for _, input := range crossChainInputs { if err := m.db.Create(input).Error; err != nil { return errors.Wrap(err, fmt.Sprintf("create DepositFromMainchain input: txid(%s), pos(%d)", tx.ID.String(), input.SourcePos)) } }
到这里,对主链到侧链的deposit已经处理完毕。
(5)跨链交易(侧链到主链的withdraw)交易处理
这部分比较复杂的逻辑主要在sidechain_keeper.go中的processWithdrawalTx函数中。这部分逻辑和上面主链到侧链的deposit逻辑相似。一样是建立了orm.crossTransaction结构体,惟一的改变就是交易的souce和dest相反。这里就不做具体描述了。
三、跨链优缺点
优势
(1) 跨链模型、代码较为完整。当前有不少项目使用跨链技术,可是真正实现跨链的寥寥无几。
(2) 能够根据不一样需求实现侧链,知足多种场景
缺点
(1) 跨链速度较慢,需等待10个区块确认,这在目前Bytom网络上所需时间为30分钟左右
(2) 相较于comos、polkadot等项目,开发者要开发侧连接入主网成本较大
(3) 只支持资产跨链,不支持跨链智能合约调用
**四、**跨链模型平行对比Cosmos
可扩展性
bystack的主测链协同工做模型依靠Federation,未造成通用协议。其余开发者想要接入其跨链网络难度较大。Cosmos采用ibc协议,可扩展性较强。
代码开发进度
vapor侧链已经可以实现跨链。Cosmos目前暂无成熟跨链项目出现,ibc协议处于最终开发阶段。
跨链模型
vapor为主侧链模型,Cosmos为Hub-Zone的中继链模型。
五、参考建议
侧链使用bbft共识,非POW的状况下,无需等待10个交易确认,增快跨链速度。
做者:诗人