package paracross

import (
	"bytes"
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
	"path/filepath"
	"time"

	"github.com/33cn/chain33/common"
	"github.com/33cn/chain33/common/address"
	"github.com/33cn/chain33/common/crypto"
	"github.com/33cn/chain33/common/db"
	"github.com/33cn/chain33/rpc/grpcclient"
	types33 "github.com/33cn/chain33/types"
	util33 "github.com/33cn/chain33/util"
	"github.com/33cn/plugin/plugin/dapp/paracross/executor"
	paracorssTypes "github.com/33cn/plugin/plugin/dapp/paracross/types"
	"github.com/hashicorp/go-hclog"
	"github.com/spf13/cast"
	"gitlab.33.cn/link33/sidecar-client-chain33/config"
	"gitlab.33.cn/link33/sidecar-client-chain33/types"
	"gitlab.33.cn/link33/sidecar-client-chain33/util"
	"gitlab.33.cn/link33/sidecar-client-chain33/util/log"
	"gitlab.33.cn/link33/sidecar/model/pb"
	"gitlab.33.cn/link33/sidecar/pkg/plugins"
	"go.uber.org/atomic"
)

var (
	_          plugins.Client = (*Paracross)(nil)
	PluginName                = paracorssTypes.ParaX
	ID                        = paracorssTypes.ParaX
	execer                    = paracorssTypes.ParaX
	logger     hclog.Logger
)

type Paracross struct {
	eventC  chan *pb.IBTP // 发送给sidecar
	chainID int32
	fromID  string
	toID    string
	ticker  *time.Ticker
	done    chan bool
	chEvent chan *types.Event
	height  *atomic.Int64  // 已经解析的高度。1、初始化时设置； 2、服务每次重启后设置
	db      *util.KVDBList // 记录已完成的hashID、高度等信息。

	// 模拟平行链节点
	paraHeight   *atomic.Uint64 // paraHeightHash = Hash(paraHeight)
	grpcClient   types33.Chain33Client
	sendMsgCh    chan *types33.Transaction // 提交共识交易
	privateKey   crypto.PrivKey
	cfg33        *types33.Chain33Config
	MinTxFeeRate int64
	title        string
	ctx          context.Context
	cancel       context.CancelFunc
	// 保存区块：(latest_height-6)高度以下的区块
	// 分类块中的交易为：已完成、未完成的交易。
	notify     chan int64 // 高度完成提醒
	heightDiff int64      // 高度差

	isTest bool
}

func (p *Paracross) incParaHeight() uint64 {
	h := p.paraHeight.Inc()
	value := fmt.Sprintf("%d", h)
	_ = p.db.Set([]byte("latest-para-height"), []byte(value))
	return h
}

func (p *Paracross) incHeight() int64 {
	h := p.height.Inc()
	value := fmt.Sprintf("%d", h)
	_ = p.db.Set([]byte("latest-height"), []byte(value))
	return h
}

func (p *Paracross) Start() error {
	p.ctx, p.cancel = context.WithCancel(context.Background())
	go p.pollAppchain()
	go p.loop()
	logger.Info("Paracross  started")
	return nil
}

func (p *Paracross) Stop() error {
	p.done <- true
	p.ticker.Stop()
	p.db.Close()
	p.cancel()
	return nil
}

func (p *Paracross) Name() string {
	// TODO implement me
	return ID
}

func (p *Paracross) Type() string {
	return ID
}

func (p *Paracross) ID() string {
	return p.fromID
}

func (p *Paracross) Initialize(configPath string, ID string, extra []byte) error {
	chain33Config, err := config.UnmarshalConfig(configPath)
	if err != nil {
		return fmt.Errorf("unmarshal config for plugin :%w", err)
	}
	out, err := log.Initialize(
		log.WithReportCaller(chain33Config.Log.ReportCaller == true),
		log.WithPersist(true),
		log.WithFilePath(filepath.Join(filepath.Dir(configPath), "logs")),
		log.WithFileName("chain33-client-"+PluginName+".log"),
		log.WithMaxSize(2*1024*1024),
		log.WithMaxAge(24*time.Hour),
		log.WithRotationTime(24*time.Hour),
	)
	if err != nil {
		return fmt.Errorf("log initialize: %w", err)
	}
	logger = hclog.New(&hclog.LoggerOptions{
		Name:   "chain33-client-" + PluginName,
		Output: out,
		Level:  hclog.LevelFromString(chain33Config.Log.Level),
	})

	// 初始化e.client工作
	p.eventC = make(chan *pb.IBTP, 10)
	p.done = make(chan bool)
	p.ticker = time.NewTicker(2 * time.Second)
	logger.Info("print chain33Config", "chain33Config", util.Sprint(chain33Config))
	logger.Info("print privateKey", "privateKey", chain33Config.Chain33.PrivateKey)
	p.privateKey = util33.HexToPrivkey(chain33Config.Chain33.PrivateKey)
	p.chainID = chain33Config.Chain33.ChainID
	p.toID = chain33Config.Router.ToChain
	p.fromID = chain33Config.Router.FromChain
	p.title = chain33Config.Chain33.Title
	p.MinTxFeeRate = chain33Config.Chain33.Fee
	p.isTest = chain33Config.Chain33.IsTest

	config33 = fmt.Sprintf(config33, p.title, chain33Config.Chain33.Addr)
	logger.Info(config33)
	p.heightDiff = chain33Config.Chain33.HeightDiff

	// 构造chain33 配置文件
	cfg33 := types33.NewChain33Config(config33)
	// 初始化chain33 client rpc
	grpcCli, err := grpcclient.NewMainChainClient(cfg33, chain33Config.Chain33.Addr)
	if err != nil {
		return err
	}
	p.grpcClient = grpcCli
	p.cfg33 = cfg33

	// 加载高度，从本地磁盘加载。
	dbConfig := chain33Config.DB
	d := db.NewDB(dbConfig.Name, dbConfig.Driver, dbConfig.DbPath, dbConfig.DbCache)
	p.db = util.NewKVDBList(d)
	// 加载高度，是否可以存在区块链上
	data, err := p.db.Get([]byte("latest-height"))
	logger.Info("latest height", "height", string(data))
	if err != nil {
		logger.Error("latest_height", "err", err)
		p.height = atomic.NewInt64(0)
	} else {
		p.height = atomic.NewInt64(cast.ToInt64(string(data)))
	}

	// data, err = p.db.Get([]byte("latest-para-height"))
	// if err != nil {
	// 	logger.Error("latest_para_height", "err", err)
	// 	p.paraHeight = atomic.NewUint64(0)
	// } else {
	// 	p.paraHeight = atomic.NewUint64(cast.ToUint64(string(data)))
	// }
	if p.height.Load() < chain33Config.Chain33.StartHeight {
		p.height = atomic.NewInt64(chain33Config.Chain33.StartHeight)
	}
	var startHeight int64 = 0
	data1, err1 := p.db.Get([]byte("start-height"))
	if err1 == nil {
		startHeight = cast.ToInt64(string(data1))
	}
	_ = p.db.Set([]byte("start-height"), []byte(fmt.Sprintf("%d", chain33Config.Chain33.StartHeight)))
	if startHeight != chain33Config.Chain33.StartHeight {
		p.height = atomic.NewInt64(chain33Config.Chain33.StartHeight)
	}

	logger.Info("startHeight ", "startHeight", startHeight)
	logger.Info("latest height", "height", p.height.Load())

	p.chEvent = make(chan *types.Event, 1)

	// 启动定时器，检查是否有遗漏的数据。

	// 检验已经完成的数据。

	return nil
}

func (p *Paracross) GetIBTP() chan *pb.IBTP {
	return p.eventC
}

// pollAppchain event from Paracross
func (p *Paracross) pollAppchain() {
	for {
		select {
		case <-p.ticker.C:
			_, err := p.grpcClient.IsSync(p.ctx, &types33.ReqNil{})
			if err != nil {
				logger.Error("call IsSync error", "error", err)
				time.Sleep(2 * time.Second)
			} else {
				header, err := p.grpcClient.GetLastHeader(p.ctx, &types33.ReqNil{})
				if err != nil {
					logger.Error("GetLastHeader call error", "error", err)
					time.Sleep(2 * time.Second)
				} else {
					logger.Info("GetLastHeader", "height", header.Height)
					lastHeight := header.Height
					height := int64(p.height.Load())
					if lastHeight-p.heightDiff <= height {
						time.Sleep(2 * time.Second)
					} else {
						p.processBlock(height+1, height+1)
					}
				}
			}

		case <-p.done:
			logger.Info("Stop long pollAppchain")
			break
		}
	}
}

func (p *Paracross) processBlock(height, end int64) {
	ret, err := p.grpcClient.GetBlocks(p.ctx, &types33.ReqBlocks{
		Start:    height,
		End:      end,
		IsDetail: true,
	})
	if !ret.IsOk || err != nil {
		logger.Error("GetBlocks call error", "height", height)
	} else {
		blockDetails := &types33.BlockDetails{}
		err = types33.Decode(ret.Msg, blockDetails)
		if err != nil {
			logger.Error("Decode error", "error", err)
		} else {
			for _, blockDetail := range blockDetails.Items {
				height1 := blockDetail.Block.GetHeight()
				logger.Info("deal height block begin-----------------------", "height", height1)
				paraTxDetail := blockDetail.FilterParaTxsByTitle(p.cfg33, p.title)
				// logger.Info("txDetail-----------------------", "txDetail", util.FormatJSON(paraTxDetail))
				paraAllTxs := executor.FilterTxsForPara(p.cfg33, paraTxDetail)
				// 所有平行链交易，平行链交易包含跨链交易，主链会打包平行交易，并不执行，仅收取交易费，后期等触发事件再执行被打包交易。
				// logger.Info("paraAllTxs-----------------------", "paraAllTxs", paraAllTxs)
				paraCrossTxs := executor.FilterParaCrossTxs(paraAllTxs) // 所有跨链交易，
				logger.Info("paraCrossTxs-----------------------", "paraCrossTxs", util.FormatJSON(paraCrossTxs))

				unDoneTx := make([][]byte, 0)
				hashData := make([]byte, 0)
				txDetails := make(map[string]*types33.TxDetail)
				for _, txDetail := range paraTxDetail.TxDetails {
					txDetails[string(txDetail.Tx.Hash())] = txDetail
				}

				for _, tx := range paraCrossTxs {
					txDetail, is := txDetails[string(tx.Hash())]
					if is {
						t, _ := types33.PBToJSON(tx)
						logger.Info("printf tx", "tx", util.FormatJSON(string(t)))

						var action paracorssTypes.ParacrossAction
						err := types33.Decode(tx.Payload, &action)
						if err != nil {
							logger.Error("Decode error")
							continue
						}

						TySelect := map[int32]map[int32]func(){
							types33.ExecOk: {
								paracorssTypes.ParacrossActionAssetWithdraw: func() {
									unlock := action.GetAssetWithdraw()
									srcEvent := &types.Event{}
									err := types33.Decode(unlock.Note, srcEvent)
									if err != nil {
										logger.Error("types33.Decode(unlock.Note, srcEvent) error:", "error", err)
									}
									p.updateBurnUnlock(srcEvent, p.unLockEvent(unlock, tx, height1))
									logger.Info("unlock <=> unLockEvent succeed!", "tx", util.FormatJSON(string(t)))
								},
								paracorssTypes.ParacrossActionAssetTransfer: func() {
									// lock 用户发起
									event := p.lockEvent(action.GetAssetTransfer(), tx, height1)
									p.saveLock(event)
									p.chEvent <- event
									logger.Info("lock <=> lockEvent succeed!", "tx", util.FormatJSON(string(t)))
								},
							},
							types33.ExecPack: {
								paracorssTypes.ParacrossActionAssetWithdraw: func() {
									// pack的交易，在只后的高度，受一定的条件触发，会再次执行，并且包含在当前高度之中的，以及执行的recipt。可以这样的获取。
									if bytes.Equal(p.privateKey.PubKey().Bytes(), tx.Signature.Pubkey) {
										unDoneTx = append(unDoneTx, tx.Hash())
										hashData = append(hashData, tx.Hash()...)
										p.saveUnLock(p.unLockEvent(action.GetAssetWithdraw(), tx, height1))
									}
								},
								paracorssTypes.ParacrossActionCommit: func() {
									// 重新发起，指定交易、以及所在高度
								},
							},
						}

						val, is := TySelect[txDetail.Receipt.Ty]
						if is {
							f, is := val[action.Ty]
							if is {
								f()
								continue
							}
						}
						logger.Error("processBlock transaction error ", "hash", util.ToHex(tx.Hash()), "txDetail:", util.FormatJSON(txDetail), "height:", height1)
					}
				}
				logger.Info("deal height block end-----------------------", "height", height1)
				if len(unDoneTx) > 0 {
					// 再发起一次确认操作
					// 提交模拟status commit事件
					ret, err := p.grpcClient.QueryChain(p.ctx,
						&types33.ChainExecutor{
							Driver:   execer,
							FuncName: "GetTitle",
							Param:    types33.Encode(&types33.ReqString{Data: p.title}),
						})
					if err != nil {
						logger.Error("ChainExecutor GetTitle error", "error", err)
					} else {
						var status paracorssTypes.ParacrossStatus
						err = types33.Decode(ret.Msg, &status)
						if err != nil {
							logger.Error("ParacrossStatus decode error", "error", err)
						} else {
							logger.Info("ParacrossStatus", "status", util.FormatJSON(status), "current main height", height)
							if height1 > status.MainHeight {
								status, err := p.getNodeStatus(unDoneTx, blockDetail, status.Height)
								if err != nil {
									logger.Error("getNodeStatus error", "error", err)
								} else {
									p.commitTxStatus(status, hashData)
								}
							} else {
								// 同步区块，查询。是否已经执行。已经确认。
								logger.Warn("is syncing block", "status", util.FormatJSON(status), "current main height", height, "para height", status.Height)
							}
						}
					}
				}
				p.saveBlock(height1, blockDetail)
				p.incHeight()
			}
		}
	}
}

func (p *Paracross) lockEvent(lock *types33.AssetsTransfer, tx *types33.Transaction, height int64) *types.Event {
	event := &types.Event{
		TxID:        util.ToHex(tx.Hash()),
		BlockNumber: uint64(height),
		Contract: &types.Contract{
			ChainID:    cast.ToString(p.chainID),
			ExecName:   ID,
			Address:    address.ExecAddress(ID), // 合约地址
			CallMethod: "AssetTransfer",
		},
		Payload: &types.MapAssetInfo{
			EventType:   types.EventType_Lock,
			LockAddress: tx.From(), // 用户地址
			LockAmount:  lock.Amount,
			Symbol:      types.Symbol(lock.Cointoken),
			Extra:       lock.Note,
			Height:      height,
			Nonce:       tx.Nonce,
		},
	}
	return event
}

func (p *Paracross) unLockEvent(unlock *types33.AssetsWithdraw, tx *types33.Transaction, height int64) *types.Event {
	event := &types.Event{
		TxID:        util.ToHex(tx.Hash()),
		BlockNumber: uint64(height),
		Contract: &types.Contract{
			ChainID:    cast.ToString(p.chainID),
			ExecName:   ID,
			Address:    address.ExecAddress(ID), // 合约地址
			CallMethod: "AssetsWithdraw",
		},
		Payload: &types.MapAssetInfo{
			EventType:     types.EventType_UnLock,
			UnlockAddress: unlock.To, // 用户地址
			UnlockAmount:  unlock.Amount,
			Symbol:        types.Symbol(unlock.Cointoken),
			Extra:         unlock.Note,
			Height:        height,
			Nonce:         tx.Nonce,
		},
	}
	return event
}

func (p *Paracross) commitTxStatus(status *paracorssTypes.ParacrossNodeStatus, hashData []byte) {
	secTx, err := p.CreateRawCommitTx(&paracorssTypes.ParacrossCommitAction{
		Status: status,
	})
	if err != nil {
		logger.Error("CreateRawCommitTx error", "error", err)
	} else {
		if len(hashData) > 0 {
			secTx.Nonce = int64(util.Hash64(hashData))
		}
		secTx.Sign(types33.SECP256K1, p.privateKey)
		ret, err := p.grpcClient.SendTransaction(p.ctx, secTx)
		if err != nil {
			logger.Error("commitTxStatus SendTransaction error", "error", err)
		} else {
			if !ret.IsOk {
				logger.Error("commitTxStatus SendTransaction false", "msg", util.ToHex(ret.Msg))
			} else {
				logger.Error("commitTxStatus SendTransaction ok", "msg", util.ToHex(ret.Msg))
			}
		}
	}
}

func (p *Paracross) saveBlock(height int64, blockDetail *types33.BlockDetail) {
	block := types33.Encode(blockDetail)
	k := fmt.Sprintf("block_%d", height)
	_ = p.db.Set([]byte(k), block)
}

func (p *Paracross) isFocus(event *types.Event) bool {
	ey := event.Payload.EventType
	switch {
	case ey == types.EventType_Lock:
		return true
	case ey == types.EventType_UnLock:
		return true
	default:
		return false
	}
}

// 保存event: key = prefix + chainID + txid ; value = event
func (p *Paracross) saveXEvent(prefix string, event *types.Event) {
	// key: prefix + chainID + txID
	key := fmt.Sprintf("%s-%s-%s", prefix, event.Contract.ChainID, event.TxID)
	data := types33.Encode(event)
	_ = p.db.Set([]byte(key), data)
	p.saveHeightEvent(event)
}

// 在联盟链一侧，用户发起销毁：1、销毁部分。2、销毁全部
func (p *Paracross) saveBurn(event *types.Event) {
	p.saveXEvent("burn", event)
}

func (p *Paracross) saveMint(event *types.Event) {
	p.saveXEvent("mint", event)
}

// 在公链一侧，用户发起铸币。
func (p *Paracross) saveLock(event *types.Event) {
	p.saveXEvent("lock", event)
}

//
func (p *Paracross) saveUnLock(event *types.Event) {
	p.saveXEvent("unlock", event)
}

// 指定高度的交易: key = "height-" + chainID + blocknumber + txid; value = txid
func (p *Paracross) saveHeightEvent(event *types.Event) {
	key := fmt.Sprintf("%d-%s-%s", event.BlockNumber, event.Contract.ChainID, event.TxID)
	_ = p.db.Set([]byte(key), []byte(event.TxID))
}

// 未完成绑定的event: key = "undone-" + txid; value = txID
func (p *Paracross) saveUndoneEvent(event *types.Event) {
	key := fmt.Sprintf("undone-%s", event.TxID)
	_ = p.db.Set([]byte(key), []byte(event.TxID))
}

// 完成绑定的event: key = "done-" + txid; value = txID1
func (p *Paracross) saveDoneEvent(event *types.Event, event1 *types.Event) {
	key := fmt.Sprintf("done-%s", event.TxID)
	_ = p.db.Set([]byte(key), []byte(event1.TxID))
}

// 已完成、指定高度的event绑定： key = "height-done" + blocknumber +  txid; value = txID1
func (p *Paracross) saveHeightDoneEvent(event *types.Event, event1 *types.Event) {
	key := fmt.Sprintf("height-done-%d-%s", event.BlockNumber, event.TxID)
	_ = p.db.Set([]byte(key), []byte(event1.TxID))
}

func (p *Paracross) deleteUndoneEvent(txID string) {
	_ = p.db.Delete([]byte(fmt.Sprintf("undone-%s", txID)))
}

// burn => unlock => ack burn
func (p *Paracross) updateBurnUnlock(burnEvent, unlockEvent *types.Event) {
	p.deleteUndoneEvent(burnEvent.TxID)
	p.deleteUndoneEvent(unlockEvent.TxID)
	p.saveDoneEvent(burnEvent, unlockEvent)
	p.saveHeightDoneEvent(burnEvent, unlockEvent)
}

// lock => mint => ack lock
func (p *Paracross) updateLockMint(lockEvent, mintEvent *types.Event) {
	p.deleteUndoneEvent(lockEvent.TxID)
	p.deleteUndoneEvent(mintEvent.TxID)
	p.saveDoneEvent(lockEvent, mintEvent)
	p.saveHeightDoneEvent(lockEvent, mintEvent)
}

// unlock => burn => ack unlock
func (p *Paracross) updateUnlockBurn(unlockEvent, burnEvent *types.Event) {
	panic("not implement me")
}

// mint => lock => ack mint
func (p *Paracross) updateMintLock(mintEvent, lockEvent *types.Event) {
	panic("not implement me")
}

// 初始状态：lock、unlock、mint、burn、
// 中间状态：unDone
// 最终状态：Done
// 备注：中间状态与最终状态互斥

func (p *Paracross) loop() {
	for event := range p.chEvent {
		logger.Debug("loop method recv", "event", util.FormatJSON(event))
		payload := pb.Payload{
			Encrypted: false,
			Content:   types33.Encode(event),
		}
		data, _ := payload.Marshal()
		tmp := time.Now().UnixNano()
		ibtp := &pb.IBTP{
			From:      p.fromID,
			To:        p.toID,
			Nonce:     uint64(event.Payload.Nonce), // TODO 唯一标识。随机nonce
			Type:      pb.IBTP_INTERCHAIN,
			Timestamp: tmp,
			Payload:   data,
			Version:   types.Version,
		}
		p.eventC <- ibtp
	}
}

func (p *Paracross) SubmitIBTP(ibtp *pb.IBTP) (*pb.SubmitIBTPResponse, error) {
	logger.Debug("SubmitIBTP recv ibtp", "ibtp", util.FormatJSON(ibtp))
	ibtpRes := &pb.SubmitIBTPResponse{}
	switch ibtp.Type {
	case pb.IBTP_INTERCHAIN:
		// 上链之前，先查询，是否存在
		event := &types.Event{}
		payload := &pb.Payload{}
		err := payload.Unmarshal(ibtp.Payload)
		if err != nil {
			return nil, err
		}
		err = types33.Decode(payload.Content, event)
		if err != nil {
			logger.Error("decode error", err)
			return nil, err
		}
		srcEvent := &types.Event{}
		if len(event.Payload.Extra) > 0 {
			err := types33.Decode(event.Payload.Extra, srcEvent)
			if err != nil {
				logger.Error("decode error", "error", err)
				return nil, err
			}
		}
		d, _ := types33.PBToJSON(event)
		logger.Debug("SubmitIBTP recv event", "event", util.FormatJSON(string(d)))
		if event.Payload.EventType == types.EventType_Mint {
			// 构造交易，请求, lock -> mint -> ack mint 整个流程，到这里已经完成。
			p.saveMint(event)
			p.updateLockMint(event, srcEvent)
		}
		if event.Payload.EventType == types.EventType_Burn {
			// 用户在联盟链一侧发起销毁。
			// 第一次提交。
			tx, err := paracorssTypes.CreateRawAssetTransferTxExt(p.chainID, p.MinTxFeeRate, &types33.CreateTx{
				To:         event.Payload.BurnAddress,
				Amount:     event.Payload.BurnAmount,
				Fee:        p.MinTxFeeRate,
				Note:       payload.Content, // 关联数据
				IsWithdraw: true,
				// TokenSymbol: event.Payload.Symbol, // TODO 销毁bty时，不要填写！！！
				ExecName: p.title + execer, // TODO 平行链执行器。
			})
			if err != nil {
				logger.Error("CreateRawAssetTransferTxExt error", "error", err)
				return nil, err
			}
			tx.Nonce = int64(ibtp.Nonce)
			tx.Sign(types33.SECP256K1, p.privateKey)
			ret, err := p.grpcClient.SendTransaction(p.ctx, tx)
			if err != nil {
				logger.Error("SendTransaction ", "error", err)
				return nil, err
			}
			ibtpRes.Result = ibtp
			if ret.IsOk {
				p.saveBurn(event)
				logger.Info("SendTransaction tx ok", "hash", string(ret.Msg))
				ibtpRes.Status = true
			} else {
				ibtpRes.Status = false
				logger.Error("SendTransaction error", "msg", string(ret.Msg))
			}
			return ibtpRes, nil
		}

	default:
		logger.Error("ibtp.Type error: ", "type", ibtp.Type)
	}
	return nil, errors.New("ibtp error")
}

// 模拟status
func (p *Paracross) getNodeStatus(curTxHashs [][]byte, blockDetail *types33.BlockDetail, paraHeight int64) (*paracorssTypes.ParacrossNodeStatus, error) {
	// var (
	// 	txs []*types33.Transaction
	// )
	// if p.isTest {
	// 	txs = blockDetail.Block.Txs
	// } else {
	// 	txs = blockDetail.Block.Txs[1:]
	// }
	txs := blockDetail.Block.Txs
	isCommitTx := make(map[string]bool)
	var allTxHashs [][]byte
	for _, tx := range txs {
		hash := tx.Hash()
		allTxHashs = append(allTxHashs, hash)
		if p.cfg33.IsMyParaExecName(string(tx.Execer)) && bytes.HasSuffix(tx.Execer, []byte(paracorssTypes.ParaX)) {
			var payload paracorssTypes.ParacrossAction
			err := types33.Decode(tx.Payload, &payload)
			if err != nil {
				return nil, err
			}
			if payload.Ty == paracorssTypes.ParacrossActionCommit {
				isCommitTx[string(hash)] = true
			}
		}
	}
	txResultBitMap := []byte(hex.EncodeToString(CalcSingleBitMap(allTxHashs)))
	logger.Info("getNodeStatus", "txResultBitMap", txResultBitMap, "allTxHashs", allTxHashs)

	// 一个块多笔unlock种类的交易，确认的时候，高度是否要求保持一致性。
	paraHeight = paraHeight + 1
	paraBlockHash := common.Sha256([]byte(cast.ToString(paraHeight)))

	hs, err := p.grpcClient.GetHeaders(p.ctx, &types33.ReqBlocks{
		Start:    blockDetail.Block.Height,
		End:      blockDetail.Block.Height,
		IsDetail: true,
		Pid:      nil,
	})
	if err != nil {
		return nil, err
	}

	return &paracorssTypes.ParacrossNodeStatus{
		MainBlockHash:   hs.Items[0].Hash,         // 跨链交易在主链时高度的hash
		MainBlockHeight: blockDetail.Block.Height, // 跨链交易在主链时的高度
		Height:          int64(paraHeight),        // 自维护，递增必须
		BlockHash:       paraBlockHash,            // Hash(height)
		TxResult:        txResultBitMap,
		Title:           p.title,
	}, nil
}

func CalcSingleBitMap(bases [][]byte) []byte {
	rst := big.NewInt(0)
	for i := range bases {
		// if data[i].Ty == types.ExecOk {
		rst.SetBit(rst, i, 1)
		// }
	}

	return rst.Bytes()
}

func CalcBitMap(bases, subs [][]byte) ([]byte, string) {
	rst := big.NewInt(0)
	subMap := make(map[string]bool)
	for _, sub := range subs {
		// if subData[i].Ty == types.ExecOk {
		subMap[string(sub)] = true
		// }
	}
	for i, base := range bases {
		if _, exist := subMap[string(base)]; exist {
			rst.SetBit(rst, i, 1)
		}
	}
	return rst.Bytes(), rst.String()
}

func (p *Paracross) CreateRawCommitTx(commit *paracorssTypes.ParacrossCommitAction) (*types33.Transaction, error) {
	action := &paracorssTypes.ParacrossAction{
		Ty:    paracorssTypes.ParacrossActionCommit,
		Value: &paracorssTypes.ParacrossAction_Commit{commit},
	}
	tx := &types33.Transaction{
		Payload: types33.Encode(action),
		To:      address.ExecAddress(execer),
		Expire:  types33.Now().Unix() + int64(120), // 120s
	}
	return p.FormatTx(tx)
}

func (p *Paracross) FormatTx(tx *types33.Transaction) (*types33.Transaction, error) {
	tx.Nonce = rand.Int63()
	tx.ChainID = p.chainID
	tx.Execer = []byte(p.title + execer)
	// 平行链，所有的to地址都是合约地址
	tx.To = address.ExecAddress(string(tx.Execer))
	var err error
	if tx.Fee == 0 {
		tx.Fee, err = tx.GetRealFee(p.MinTxFeeRate)
		if err != nil {
			return nil, err
		}
	}
	return tx, nil
}
