package synchron
import (
+ "bytes"
"encoding/hex"
+ "fmt"
"time"
+ btmBc "github.com/bytom/protocol/bc"
btmTypes "github.com/bytom/protocol/bc/types"
"github.com/jinzhu/gorm"
log "github.com/sirupsen/logrus"
"github.com/vapor/protocol/bc"
)
-var ErrInconsistentDB = errors.New("inconsistent db status")
-
type mainchainKeeper struct {
cfg *config.Chain
db *gorm.DB
blockHash := block.Hash()
log.WithFields(log.Fields{"block_height": block.Height, "block_hash": blockHash.String()}).Info("start to attachBlock")
m.db.Begin()
- if err := m.processBlock(chain, block); err != nil {
+ if err := m.processBlock(chain, block, txStatus); err != nil {
m.db.Rollback()
return err
}
return m.db.Commit().Error
}
-func (m *mainchainKeeper) processBlock(chain *orm.Chain, block *btmTypes.Block) error {
+func (m *mainchainKeeper) processBlock(chain *orm.Chain, block *btmTypes.Block, txStatus *bc.TransactionStatus) error {
if err := m.processIssuing(block.Transactions); err != nil {
return err
}
- // for i, tx := range txs {
- // if isDepositFromMainchain(tx) {
- // bp.processDepositFromMainchain(uint64(i), tx)
- // }
- // if isWithdrawalToMainchain(tx) {
- // bp.processWithdrawalToMainchain(uint64(i), tx)
- // }
- // }
+ 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(uint64(i), tx); err != nil {
+ return err
+ }
+ }
+ }
return m.processChainInfo(chain, block)
}
+func (m *mainchainKeeper) isDepositTx(tx *btmTypes.Tx) bool {
+ for _, output := range tx.Outputs {
+ if bytes.Equal(output.OutputCommitment.ControlProgram, fedProg) {
+ return true
+ }
+ }
+ return false
+}
+
+func (m *mainchainKeeper) isWithdrawalTx(tx *btmTypes.Tx) bool {
+ for _, input := range tx.Inputs {
+ if bytes.Equal(input.ControlProgram(), fedProg) {
+ return true
+ }
+ }
+ return false
+}
+
+func (m *mainchainKeeper) processDepositTx(chain *orm.Chain, block *btmTypes.Block, txStatus *bc.TransactionStatus, txIndex uint64, tx *btmTypes.Tx) error {
+ blockHash := block.Hash()
+
+ var muxID btmBc.Hash
+ isMuxIDFound := false
+ for _, resOutID := range tx.ResultIds {
+ resOut, ok := tx.Entries[*resOutID].(*btmBc.Output)
+ if ok {
+ muxID = *resOut.Source.Ref
+ isMuxIDFound = true
+ break
+ }
+ }
+ if !isMuxIDFound {
+ return errors.New("fail to get mux id")
+ }
+
+ rawTx, err := tx.MarshalText()
+ if err != nil {
+ return err
+ }
+
+ ormTx := &orm.CrossTransaction{
+ ChainID: chain.ID,
+ BlockHeight: block.Height,
+ BlockHash: blockHash.String(),
+ TxIndex: txIndex,
+ MuxID: muxID.String(),
+ TxHash: tx.ID.String(),
+ RawTransaction: string(rawTx),
+ // Status uint8
+ }
+ 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.getCrossChainInputs(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
+}
+
+func (m *mainchainKeeper) getCrossChainInputs(mainchainTxID uint64, tx *btmTypes.Tx, statusFail bool) ([]*orm.CrossTransactionInput, error) {
+ // assume inputs are from an identical owner
+ script := hex.EncodeToString(tx.Inputs[0].ControlProgram())
+ inputs := []*orm.CrossTransactionInput{}
+ for i, rawOutput := range tx.Outputs {
+ // check valid deposit
+ if !bytes.Equal(rawOutput.OutputCommitment.ControlProgram, fedProg) {
+ continue
+ }
+
+ if statusFail && *rawOutput.OutputCommitment.AssetAmount.AssetId != *btmConsensus.BTMAssetID {
+ continue
+ }
+
+ asset, err := m.getAsset(rawOutput.OutputCommitment.AssetAmount.AssetId.String())
+ if err != nil {
+ return nil, err
+ }
+
+ // default null SidechainTxID, which will be set after submitting deposit tx on sidechain
+ input := &orm.CrossTransactionInput{
+ MainchainTxID: mainchainTxID,
+ SourcePos: uint64(i),
+ AssetID: asset.ID,
+ AssetAmount: rawOutput.OutputCommitment.AssetAmount.Amount,
+ Script: script,
+ }
+ inputs = append(inputs, input)
+ }
+ return inputs, nil
+}
+
+func (m *mainchainKeeper) processWithdrawalTx(txIndex uint64, tx *btmTypes.Tx) error {
+ return nil
+}
+
+// TODO: maybe common
func (m *mainchainKeeper) processChainInfo(chain *orm.Chain, block *btmTypes.Block) error {
blockHash := block.Hash()
chain.BlockHash = blockHash.String()
return nil
}
+// TODO: maybe common
func (m *mainchainKeeper) getAsset(assetID string) (*orm.Asset, error) {
if asset := m.assetCache.Get(assetID); asset != nil {
return asset, nil