func GetNextBlockSigner(height uint32, timestamp int64) ([]byte, []byte, pb.WinnerType, error) {
	currentHeight := DefaultLedger.Store.GetHeight()
	if height > currentHeight {
		return nil, nil, 0, fmt.Errorf("Height %d is higher than current height %d", height, currentHeight)
	}
	headerHash := DefaultLedger.Store.GetHeaderHashByHeight(height)
	header, err := DefaultLedger.Store.GetHeader(headerHash)
	if err != nil {
		return nil, nil, 0, err
	}
	var publicKey []byte
	var chordID []byte
	winnerType := header.UnsignedHeader.WinnerType
	if winnerType == pb.WinnerType_GENESIS_SIGNER {
		genesisBlockHash, err := DefaultLedger.Store.GetBlockHash(0)
		if err != nil {
			return nil, nil, 0, err
		}
		genesisBlock, err := DefaultLedger.Store.GetBlock(genesisBlockHash)
		if err != nil {
			return nil, nil, 0, err
		}
		publicKey, chordID, err = genesisBlock.Header.GetSigner()
		if err != nil {
			return nil, nil, 0, err
		}
		return publicKey, chordID, pb.WinnerType_GENESIS_SIGNER, nil
	}
	if timestamp <= header.UnsignedHeader.Timestamp {
		return nil, nil, 0, fmt.Errorf("timestamp %d is earlier than previous block timestamp %d", timestamp, header.UnsignedHeader.Timestamp)
	}
	timeSinceLastBlock := timestamp - header.UnsignedHeader.Timestamp
	proposerChangeTime := int64(config.ConsensusTimeout.Seconds())
	if timeSinceLastBlock >= proposerChangeTime {
		if timeSinceLastBlock%proposerChangeTime > int64(ProposingTimeTolerance.Seconds()) {
			return nil, nil, 0, nil
		}
		winnerType = pb.WinnerType_BLOCK_SIGNER
		proposerBlockHeight := int64(DefaultLedger.Store.GetHeight()) - timeSinceLastBlock/proposerChangeTime
		if proposerBlockHeight < 0 {
			proposerBlockHeight = 0
		}
		proposerBlockHash, err := DefaultLedger.Store.GetBlockHash(uint32(proposerBlockHeight))
		if err != nil {
			return nil, nil, 0, err
		}
		proposerBlockHeader, err := DefaultLedger.Store.GetHeader(proposerBlockHash)
		if err != nil {
			return nil, nil, 0, err
		}
		publicKey, chordID, err = proposerBlockHeader.GetSigner()
		if err != nil {
			return nil, nil, 0, err
		}
	} else {
		if timeSinceLastBlock < int64(config.ConsensusDuration.Seconds()) || timeSinceLastBlock > int64(config.ConsensusDuration.Seconds())+int64(ProposingTimeTolerance.Seconds()) {
			return nil, nil, 0, nil
		}
		switch winnerType {
		case pb.WinnerType_TXN_SIGNER:
			whash, err := common.Uint256ParseFromBytes(header.UnsignedHeader.WinnerHash)
			if err != nil {
				return nil, nil, 0, err
			}
			txn, err := DefaultLedger.Store.GetTransaction(whash)
			if err != nil {
				return nil, nil, 0, err
			}
			if txn.UnsignedTx.Payload.Type != pb.PayloadType_SIG_CHAIN_TXN_TYPE {
				return nil, nil, 0, errors.New("invalid transaction type")
			}
			payload, err := transaction.Unpack(txn.UnsignedTx.Payload)
			if err != nil {
				return nil, nil, 0, err
			}
			sigChainTxn := payload.(*pb.SigChainTxn)
			sigChain := &pb.SigChain{}
			err = proto.Unmarshal(sigChainTxn.SigChain, sigChain)
			if err != nil {
				return nil, nil, 0, err
			}
			blockHash, err := common.Uint256ParseFromBytes(sigChain.BlockHash)
			if err != nil {
				return nil, nil, 0, err
			}
			height, err := DefaultLedger.Store.GetHeightByBlockHash(blockHash)
			if err != nil {
				return nil, nil, 0, err
			}
			publicKey, chordID, err = sigChain.GetMiner(height, header.UnsignedHeader.RandomBeacon)
			if err != nil {
				return nil, nil, 0, err
			}
		case pb.WinnerType_BLOCK_SIGNER:
		}
	}
	return publicKey, chordID, winnerType, nil
}
GetNextBlockSigner gets the next block signer after block height at
timestamp. Returns next signer's public key, chord ID, winner type, and error
package chain
import (
	"bytes"
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"hash/fnv"
	"time"
	"github.com/nknorg/nkn/v2/chain/txvalidator"
	"github.com/golang/protobuf/proto"
	"github.com/nknorg/nkn/v2/block"
	"github.com/nknorg/nkn/v2/common"
	"github.com/nknorg/nkn/v2/config"
	"github.com/nknorg/nkn/v2/crypto"
	"github.com/nknorg/nkn/v2/pb"
	"github.com/nknorg/nkn/v2/por"
	"github.com/nknorg/nkn/v2/program"
	"github.com/nknorg/nkn/v2/transaction"
	"github.com/nknorg/nkn/v2/util"
)
const (
	TimestampToleranceFuture   = config.ConsensusDuration / 4
	TimestampTolerancePast     = config.ConsensusDuration / 2
	TimestampToleranceVariance = config.ConsensusDuration / 6
	ProposingTimeTolerance     = config.ConsensusDuration / 2
	NumGenesisBlocks           = 4
)
var (
	ErrIDRegistered           = errors.New("id has been registered")
	ErrDuplicateGenerateIDTxn = errors.New("duplicate GenerateID txns")
	ErrDuplicateIssueAssetTxn = errors.New("duplicate IssueAsset txns")
	timestampToleranceSalt    = util.RandomBytes(32)
)
type VBlock struct {
	Block       *block.Block
	ReceiveTime int64
}
type TransactionArray []*transaction.Transaction
func (iterable TransactionArray) Iterate(handler func(item *transaction.Transaction) error) error {
	for _, item := range iterable {
		result := handler(item)
		if result != nil {
			return result
		}
	}
	return nil
}
func TransactionCheck(ctx context.Context, block *block.Block, fastSync bool) error {
	if block.IsTxnsChecked {
		return nil
	}
	if len(block.Transactions) == 0 {
		return errors.New("empty block")
	}
	numTxn := len(block.Transactions)
	if numTxn > config.MaxNumTxnPerBlock {
		return errors.New("block contains too many transactions")
	}
	blockSize := block.GetTxsSize()
	if blockSize > config.MaxBlockSize {
		return errors.New("serialized block is too big")
	}
	if block.Transactions[0].UnsignedTx.Payload.Type != pb.PayloadType_COINBASE_TYPE {
		return errors.New("first transaction in block is not Coinbase")
	}
	txnsHash := make([]common.Uint256, len(block.Transactions))
	for i, txn := range block.Transactions {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
		txnsHash[i] = txn.Hash()
	}
	txnsRoot, err := crypto.ComputeRoot(txnsHash)
	if err != nil {
		return fmt.Errorf("compute txns root error: %v", err)
	}
	if !bytes.Equal(txnsRoot.ToArray(), block.Header.UnsignedHeader.TransactionsRoot) {
		return fmt.Errorf("computed txn root %x is different from txn root in header %x", txnsRoot.ToArray(), block.Header.UnsignedHeader.TransactionsRoot)
	}
	if fastSync {
		for _, txn := range block.Transactions {
			err = txn.VerifySignature()
			if err != nil {
				return fmt.Errorf("[VerifyTransaction] %v", err)
			}
		}
		return nil
	}
	bvs := NewBlockValidationState()
	nonces := make(map[common.Uint160]uint64, 0)
	for i, txn := range block.Transactions {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
		if i != 0 && txn.UnsignedTx.Payload.Type == pb.PayloadType_COINBASE_TYPE {
			return errors.New("coinbase transaction order is incorrect")
		}
		if err := txvalidator.VerifyTransaction(txn, block.Header.UnsignedHeader.Height); err != nil {
			return fmt.Errorf("transaction sanity check failed: %v", err)
		}
		if err := bvs.VerifyTransactionWithBlock(txn, block.Header.UnsignedHeader.Height); err != nil {
			bvs.Reset()
			return fmt.Errorf("transaction block check failed: %v", err)
		}
		bvs.Commit()
		if err := VerifyTransactionWithLedger(txn, block.Header.UnsignedHeader.Height); err != nil {
			return fmt.Errorf("transaction history check failed: %v", err)
		}
		switch txn.UnsignedTx.Payload.Type {
		case pb.PayloadType_COINBASE_TYPE:
		case pb.PayloadType_SIG_CHAIN_TXN_TYPE:
		case pb.PayloadType_NANO_PAY_TYPE:
		default:
			addr, err := common.ToCodeHash(txn.Programs[0].Code)
			if err != nil {
				return err
			}
			if _, ok := nonces[addr]; !ok {
				nonce := DefaultLedger.Store.GetNonce(addr)
				nonces[addr] = nonce
			}
			if nonces[addr] != txn.UnsignedTx.Nonce {
				return fmt.Errorf("txn nonce error, expected: %v, Get: %v", nonces[addr], txn.UnsignedTx.Nonce)
			}
			nonces[addr]++
		}
	}
	bvs.Close()
	winnerHash, err := common.Uint256ParseFromBytes(block.Header.UnsignedHeader.WinnerHash)
	if err != nil {
		return err
	}
	if winnerHash != common.EmptyUint256 {
		found := false
		for _, txnHash := range txnsHash {
			if txnHash == winnerHash {
				found = true
				break
			}
		}
		if !found {
			if _, err = DefaultLedger.Store.GetTransaction(winnerHash); err != nil {
				return fmt.Errorf("mining sigchain txn %s not found in block", winnerHash.ToHexString())
			}
		}
	}
	root, err := DefaultLedger.Store.GenerateStateRoot(ctx, block, true, false)
	if err != nil {
		return err
	}
	headerRoot, _ := common.Uint256ParseFromBytes(block.Header.UnsignedHeader.StateRoot)
	if ok := root.CompareTo(headerRoot); ok != 0 {
		return fmt.Errorf("[TransactionCheck]state root not equal:%v, %v", root.ToHexString(), headerRoot.ToString())
	}
	block.IsTxnsChecked = true
	return nil
}