github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/radar/mainchain/league_consumes.go (about)

     1  package mainchain
     2  
     3  import (
     4  	"log"
     5  	"math/big"
     6  	"sync"
     7  
     8  	"fmt"
     9  
    10  	"github.com/sixexorg/magnetic-ring/common"
    11  	maintypes "github.com/sixexorg/magnetic-ring/core/mainchain/types"
    12  	orgtypes "github.com/sixexorg/magnetic-ring/core/orgchain/types"
    13  
    14  	"time"
    15  
    16  	"bytes"
    17  
    18  	"sort"
    19  
    20  	"github.com/sixexorg/magnetic-ring/errors"
    21  	"github.com/sixexorg/magnetic-ring/store/mainchain/extstorages"
    22  	"github.com/sixexorg/magnetic-ring/store/storelaw"
    23  )
    24  
    25  var (
    26  	leagueConsumers    *LeagueConsumers
    27  	unlockEnergy         = big.NewInt(0)
    28  	txTypeForReference = make(map[orgtypes.TransactionType]struct{})
    29  	txTypeForTransfer  = make(map[orgtypes.TransactionType]struct{})
    30  )
    31  
    32  func init() {
    33  	txTypeForReference[orgtypes.EnergyFromMain] = struct{}{}
    34  	txTypeForReference[orgtypes.Join] = struct{}{}
    35  
    36  	txTypeForTransfer[orgtypes.EnergyToMain] = struct{}{}
    37  }
    38  
    39  type LeagueConsumers struct {
    40  	m *sync.Mutex
    41  
    42  	lgHtCache map[common.Address]uint64 //last get league height cache
    43  
    44  	blockCh   chan *orgtypes.Block
    45  	consumers map[common.Address]*LeagueConsumer // Circle address  Circle consumer
    46  
    47  	isminer  bool
    48  	adpter   *ConsumerAdapter
    49  	ledgeror storelaw.Ledger4Validation
    50  
    51  	//These properties are designed to perform switching logic,
    52  	// but this logic is not needed now and is now used for unit tests
    53  	wg         *sync.WaitGroup
    54  	lighthouse bool //Notifies the program that this round of calculations will be closed
    55  	leagueSize int                               // Number of circles
    56  	stopSignal chan common.Address
    57  	txs        maintypes.Transactions
    58  }
    59  
    60  func NewLeagueConsumers(adpter *ConsumerAdapter) *LeagueConsumers {
    61  	leagueConsumers = &LeagueConsumers{
    62  		m:          new(sync.Mutex),
    63  		consumers:  make(map[common.Address]*LeagueConsumer),
    64  		blockCh:    make(chan *orgtypes.Block, 10000), //todo Need to control the details of the upper limit, add later
    65  		stopSignal: make(chan common.Address, 1000),
    66  		wg:         new(sync.WaitGroup),
    67  		adpter:     adpter,
    68  	}
    69  	go leagueConsumers.routing()
    70  	//actor := NewMainRadarActor(leagueConsumers.ReceiveBlock)
    71  
    72  	//pid, err := startActor(actor, "main radar actor")
    73  	return leagueConsumers
    74  }
    75  
    76  func GetLeagueConsumersInstance() *LeagueConsumers {
    77  	return leagueConsumers
    78  }
    79  
    80  func (this *LeagueConsumers) CommitLastHeight() {
    81  	for _, v := range this.consumers {
    82  		v.commitLastHeight()
    83  	}
    84  }
    85  func (this *LeagueConsumers) RollbackWillDo() {
    86  	for _, v := range this.consumers {
    87  		v.rollbackWillDo()
    88  	}
    89  }
    90  
    91  func (this *LeagueConsumers) GetLeagueHeight() map[common.Address]uint64 {
    92  	re := make(map[common.Address]uint64)
    93  	for k, v := range this.consumers {
    94  		curHeight := v.currentHeight()
    95  		re[k] = curHeight
    96  	}
    97  	this.lgHtCache = re
    98  	return re
    99  }
   100  func (this *LeagueConsumers) GenerateMainTxs() (maintypes.Transactions, error) {
   101  	return this.GenerateMainTxByLeagueHeight(nodeLHC.getNodeLHs())
   102  }
   103  
   104  func (this *LeagueConsumers) GenerateMainTxByLeagueHeight(earth map[common.Address]uint64, nodes []map[common.Address]uint64) (maintypes.Transactions, error) {
   105  	local := this.GetLeagueHeight()
   106  	lh := this.interceptHeight(local, earth, nodes...)
   107  	for k, v := range lh {
   108  		fmt.Println("πŸ”— πŸ”— πŸ”— key:", k.ToString(), " height:", v)
   109  	}
   110  	txs, err := this.generateMainTx(lh)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	/*	this.adpter.initTeller()
   115  		this.adpter.TxPoolTlr.Tell(txpool.MustPackTxsReq{
   116  			Txs: txs,
   117  		})*/
   118  	return txs, nil
   119  }
   120  
   121  type nodeHeight struct {
   122  	ndIdx int
   123  	h     uint64
   124  }
   125  
   126  //get
   127  
   128  func (this *LeagueConsumers) interceptHeight(local, earth map[common.Address]uint64, nodes ...map[common.Address]uint64) map[common.Address]uint64 {
   129  	l := len(local)
   130  	if l == 0 {
   131  		return nil
   132  	}
   133  	result := make(map[common.Address]uint64, l)
   134  	rl := uint64(len(nodes))
   135  	idx := rl / 2
   136  	referenceIdx := make([]int, 0, idx+1)
   137  	realm := this.min(local, earth)
   138  	for ko, v := range realm {
   139  		tmpV := v
   140  		if tmpV == 0 {
   141  			result[ko] = tmpV
   142  		} else {
   143  			if len(referenceIdx) == 0 {
   144  				hsort := make([]*nodeHeight, 0, rl)
   145  				for ndIdx, remote := range nodes {
   146  					hsort = append(hsort, &nodeHeight{ndIdx: ndIdx, h: remote[ko]})
   147  				}
   148  				sort.Slice(hsort, func(i, j int) bool {
   149  					return hsort[i].h < hsort[j].h
   150  				})
   151  				for _, rv := range hsort[idx:] {
   152  					referenceIdx = append(referenceIdx, rv.ndIdx)
   153  				}
   154  				if hsort[idx].h > tmpV {
   155  					result[ko] = tmpV
   156  				} else {
   157  					result[ko] = hsort[idx].h
   158  				}
   159  			} else {
   160  				hs := make([]uint64, 0, idx+1)
   161  				for _, rv := range referenceIdx {
   162  					hs = append(hs, nodes[rv][ko])
   163  				}
   164  				sort.Slice(hs, func(i, j int) bool {
   165  					return hs[i] < hs[j]
   166  				})
   167  				if hs[0] > tmpV {
   168  					result[ko] = tmpV
   169  				} else {
   170  					result[ko] = hs[0]
   171  				}
   172  			}
   173  		}
   174  	}
   175  	return result
   176  }
   177  func (this *LeagueConsumers) min(local, earth map[common.Address]uint64) map[common.Address]uint64 {
   178  	result := make(map[common.Address]uint64, len(local))
   179  	for k, v := range local {
   180  		for ki, vi := range earth {
   181  			if k.Equals(ki) {
   182  				if v < vi {
   183  					result[k] = v
   184  				} else {
   185  					result[k] = vi
   186  				}
   187  			}
   188  		}
   189  	}
   190  	return result
   191  }
   192  func (this *LeagueConsumers) generateMainTx(leagueHM map[common.Address]uint64) (maintypes.Transactions, error) {
   193  	mtcCh := make(chan *MainTxCradle, 50)
   194  	leagueLen := len(leagueHM)
   195  	wg := new(sync.WaitGroup)
   196  	wg.Add(leagueLen)
   197  	wg2 := new(sync.WaitGroup)
   198  	for k, v := range leagueHM {
   199  		lgcmer, ok := this.consumers[k]
   200  		if ok {
   201  			go func() {
   202  				mtc, err := lgcmer.generateMainTx(v)
   203  				if err != nil {
   204  					//todo log
   205  					fmt.Println("πŸ’” ", lgcmer.leagueId.ToString(), " generate failed")
   206  					wg.Done()
   207  					return
   208  				}
   209  				wg2.Add(1)
   210  				mtcCh <- mtc
   211  				wg.Done()
   212  			}()
   213  		} else {
   214  			wg.Done()
   215  		}
   216  	}
   217  	txs := make(maintypes.Transactions, 0, leagueLen*5)
   218  	go func() {
   219  		for ch := range mtcCh {
   220  			txs = append(txs, ch.Check)
   221  			txs = append(txs, ch.ReferenceTxs...)
   222  			wg2.Done()
   223  		}
   224  	}()
   225  
   226  	wg.Wait()
   227  	wg2.Wait()
   228  	close(mtcCh)
   229  	return txs, nil
   230  }
   231  
   232  type objTxForCheck struct {
   233  	agg  *maintypes.Transaction
   234  	sons maintypes.Transactions
   235  }
   236  
   237  /*func (this *LeagueConsumers) CheckLeaguesWithBoolResponse(txs maintypes.Transactions, timeout time.Duration) bool {
   238  	ch := make(chan bool, 1)
   239  	go func() {
   240  		lsp := NewLeagueStatePipe()
   241  		ch <- this.checkLeaguesResult(this.ConvertTxsToObjTxForCheck(txs), lsp, false)
   242  
   243  	}()
   244  	select {
   245  	case re := <-ch:
   246  		return re
   247  	case <-time.After(timeout):
   248  		return false
   249  	}
   250  }*/
   251  func (this *LeagueConsumers) CheckLeaguesWithBoolResponse(txs maintypes.Transactions, timeout time.Duration) error {
   252  	ch := make(chan bool, 1)
   253  	go func() {
   254  		lsp := NewLeagueStatePipe()
   255  		objTx := this.ConvertTxsToObjTxForCheck(txs)
   256  		leagueLen := len(objTx)
   257  		this.checkLeaguesResult(objTx, lsp, true)
   258  		for {
   259  			select {
   260  			case <-lsp.Successed:
   261  				leagueLen--
   262  				if leagueLen == 0 {
   263  					fmt.Println("πŸ“‘ ⏰ CheckLeaguesWithBoolResponse break")
   264  					ch <- true
   265  					break
   266  				}
   267  			}
   268  		}
   269  	}()
   270  	select {
   271  	case <-ch:
   272  		fmt.Println("πŸ“‘ ⏰ CheckLeaguesWithBoolResponse <- ch")
   273  		this.CommitLastHeight()
   274  		return nil
   275  	case <-time.After(timeout):
   276  		this.RollbackWillDo()
   277  		return errors.ERR_TIME_OUT
   278  	}
   279  }
   280  
   281  func (this *LeagueConsumers) CheckLeaguesResult(txs maintypes.Transactions, lsp *LeagueStatePipe) int {
   282  	lm := this.ConvertTxsToObjTxForCheck(txs)
   283  	this.checkLeaguesResult(lm, lsp, true)
   284  	return len(lm)
   285  }
   286  
   287  func (this *LeagueConsumers) ConvertTxsToObjTxForCheck(txs maintypes.Transactions) map[common.Address]*objTxForCheck {
   288  	leagueTxs := make(map[common.Address]*objTxForCheck)
   289  	for _, v := range txs {
   290  		tmpTX := v
   291  		if leagueTxs[tmpTX.TxData.LeagueId] == nil {
   292  			leagueTxs[tmpTX.TxData.LeagueId] = &objTxForCheck{}
   293  		}
   294  		if tmpTX.TxType == maintypes.ConsensusLeague || tmpTX.TxType == maintypes.LockLeague {
   295  			leagueTxs[tmpTX.TxData.LeagueId].agg = tmpTX
   296  		} else {
   297  			leagueTxs[tmpTX.TxData.LeagueId].sons = append(leagueTxs[tmpTX.TxData.LeagueId].sons, tmpTX)
   298  		}
   299  	}
   300  	return leagueTxs
   301  }
   302  
   303  func (this *LeagueConsumers) checkLeaguesResult(leagueTxs map[common.Address]*objTxForCheck, lsp *LeagueStatePipe, withState bool) bool {
   304  	for k, v := range leagueTxs {
   305  		go func(league common.Address, otfc *objTxForCheck) {
   306  			if this.consumers[league] == nil {
   307  				flag := true
   308  				for {
   309  					if flag {
   310  						flag = false
   311  						lsp.sendLeagueNeed(league, 1)
   312  					}
   313  					time.Sleep(time.Millisecond * 100)
   314  					if this.consumers[league] != nil {
   315  						fmt.Printf("🚫 πŸ”  check league first fill successed,the id is %s\n", league.ToString())
   316  						break
   317  					}
   318  				}
   319  			}
   320  			this.consumers[league].checkLeagueResultPart2(otfc, this.adpter.Ledger, lsp, withState)
   321  		}(k, v)
   322  	}
   323  	/*	if !withState {
   324  		success := make(chan bool, 1)
   325  		count := len(leagueTxs)
   326  		go func(ch chan bool, c int) {
   327  			for such := range lsp.Successed {
   328  				if leagueTxs[such] != nil {
   329  					c--
   330  					if c == 0 {
   331  						ch <- true
   332  					}
   333  				}
   334  			}
   335  		}(success, count)
   336  		go func(ch chan bool) {
   337  			for errch := range lsp.StateSignal {
   338  				switch errch.(type) {
   339  				case *LeagueNeed:
   340  				case *LeagueExec:
   341  				case *LeagueErr:
   342  					ch <- false
   343  				}
   344  			}
   345  		}(success)
   346  		select {
   347  		case re := <-success:
   348  			return re
   349  		}
   350  	}*/
   351  	return false
   352  }
   353  
   354  //checkLeagueResult is verify league height
   355  //otfc: mainTxs create by consensus, ledger:storage,lsp:channel response,withState:callback which league height need synchronous,true for p2p,false for consensus
   356  func (this *LeagueConsumer) checkLeagueResultPart2(otfc *objTxForCheck, ledger *extstorages.LedgerStoreImp, lsp *LeagueStatePipe, withState bool) {
   357  	/*ch := make(chan bool, 1)
   358  	go func() {*/
   359  	start := otfc.agg.TxData.StartHeight
   360  	end := otfc.agg.TxData.EndHeight
   361  	isLock := otfc.agg.TxType == maintypes.LockLeague
   362  	root := otfc.agg.TxData.BlockRoot
   363  	var err error
   364  	if this.currentHeight() >= end {
   365  		goto DOIT
   366  	} else {
   367  		//todo Waiting for calculation, less logic
   368  		//lsp.sendLeagueExec(this.leagueId, this.currentHeight())
   369  		sendCache := make(map[uint64]bool)
   370  		for {
   371  			cHeight := this.currentHeight()
   372  			fmt.Println("🚫 checkLeagueResultPart2 loop ", cHeight)
   373  			if cHeight >= end {
   374  				fmt.Println("🚫 checkLeagueResultPart2 break ", cHeight)
   375  				break
   376  			} else if withState {
   377  				targetH := cHeight + 1
   378  				if !sendCache[targetH] {
   379  					sendCache[targetH] = true
   380  					fmt.Printf("🚫 checkLeagueResultPart2 send need:%d target:%d \n", targetH, end)
   381  					lsp.sendLeagueNeed(this.leagueId, targetH)
   382  				}
   383  			}
   384  			time.Sleep(time.Millisecond * 100)
   385  		}
   386  		fmt.Println("🚫 checkLeagueResultPart2 go doit")
   387  		goto DOIT
   388  	}
   389  DOIT:
   390  	fmt.Println("🚫 checkLeagueResultPart2 doit run")
   391  	err = this.checkInterval(start, end, root, isLock, withState, ledger)
   392  	if err != nil {
   393  		lsp.sendError(this.leagueId, err)
   394  
   395  	} else {
   396  		this.willdo = otfc.agg.TxData.EndHeight
   397  		lsp.sendOk(this.leagueId)
   398  	}
   399  }
   400  
   401  func (this *LeagueConsumer) getLeagueState() uint64 {
   402  	return this.currentHeight()
   403  }
   404  
   405  func (this *LeagueConsumer) checkInterval(start, end uint64, root common.Hash, islock, offsetUpdate bool, ledger *extstorages.LedgerStoreImp) error {
   406  	hashes := make(common.HashArray, 0, end-start+1)
   407  	needRemove := make([]uint64, 0, end-start+1)
   408  	for i := start; i <= end; i++ {
   409  		h, err := ledger.GetBlockHashByHeight4V(this.leagueId, i)
   410  		if err != nil {
   411  			//TODO Inconsistent preservation, need to be processed, a few
   412  		}
   413  		hashes = append(hashes, h)
   414  		needRemove = append(needRemove, i)
   415  	}
   416  	hashRoot := hashes.GetHashRoot()
   417  	if bytes.Equal(hashRoot[:], root[:]) {
   418  		if (this.lockerr != nil) == islock {
   419  			/*	if offsetUpdate {
   420  				this.setLastHeight(end)
   421  				this.removeMainNewBefore(needRemove)
   422  			}*/
   423  			return nil
   424  		}
   425  	}
   426  	err := fmt.Errorf("diff between local and remote")
   427  	return err
   428  }
   429  
   430  //SupplementLeagueConsumer is used to synchronize lost cross-chain storage
   431  func (this *LeagueConsumers) SupplementLeagueConsumer(leagueId common.Address, blkHash common.Hash, height uint64) {
   432  	league := this.getLeagueConsume(leagueId)
   433  	if height == 1 {
   434  
   435  		if !this.isminer && !this.lighthouse {
   436  			//fmt.Println("πŸ”† πŸ“‘ SupplementLeagueConsumer 1")
   437  			go func(league *LeagueConsumer) {
   438  				league.registerConsume(this.setStopSignal, this.getLightHouse)
   439  			}(league)
   440  		}
   441  	}
   442  	league.setBlockCurrentBySync(blkHash, height)
   443  }
   444  
   445  func (this *LeagueConsumers) ReceiveBlock(block *orgtypes.Block) {
   446  	//fmt.Println("πŸ”† πŸ“‘ πŸ”†  1")
   447  	this.blockCh <- block
   448  	/*this.adpter.initTeller()
   449  	this.adpter.P2pTlr.Tell(&p2pcommon.OrgPendingData{
   450  		BANodeSrc: false,                 // true:ANode send staller false:staller send staller
   451  		OrgId:     block.Header.LeagueId, // orgid
   452  		Block:     block,                 // tx
   453  	})*/
   454  	//fmt.Println("πŸ”† πŸ“‘ πŸ”†  2")
   455  }
   456  func (this *LeagueConsumers) setStopSignal(leagueId common.Address, tx *maintypes.Transaction) {
   457  	log.Println("setStopSignal:", leagueId.ToString())
   458  	this.stopSignal <- leagueId
   459  	if tx != nil {
   460  		this.txs = append(this.txs, tx)
   461  	}
   462  }
   463  func (this *LeagueConsumers) getLightHouse() bool {
   464  	return this.lighthouse
   465  }
   466  
   467  //NewRound  is to actively launch validation
   468  func (this *LeagueConsumers) NewRound() {
   469  	this.m.Lock()
   470  	defer this.m.Unlock()
   471  	{
   472  		this.isminer = true
   473  		this.refresh()
   474  		for _, v := range this.consumers {
   475  			v.registerConsume(this.setStopSignal, this.getLightHouse)
   476  		}
   477  	}
   478  }
   479  
   480  //Truncate is to notifies the program that this round of calculations will be closed
   481  func (this *LeagueConsumers) Settlement() {
   482  	this.m.Lock()
   483  	defer this.m.Unlock()
   484  	{
   485  		if this.lighthouse {
   486  			return
   487  		}
   488  		this.lighthouse = true
   489  		log.Println("leagueSize:", this.leagueSize)
   490  		this.wg.Add(this.leagueSize)
   491  		this.await()
   492  		return
   493  	}
   494  }
   495  
   496  //await is asynchronous waiting
   497  func (this *LeagueConsumers) await() {
   498  	go func() {
   499  		for ch := range this.stopSignal {
   500  			log.Println(ch.ToString(), " over")
   501  			this.wg.Done()
   502  		}
   503  	}()
   504  	this.wg.Wait()
   505  }
   506  
   507  func (this *LeagueConsumers) refresh() {
   508  	this.lighthouse = false
   509  	this.txs = nil
   510  	this.stopSignal = make(chan common.Address, 1000)
   511  }
   512  
   513  func (this *LeagueConsumers) routing() {
   514  	for block := range this.blockCh {
   515  		//todo need check
   516  		//fmt.Println("πŸ”† πŸ“‘ routing 1")
   517  		league := this.getLeagueConsume(block.Header.LeagueId)
   518  		//fmt.Println("πŸ”† πŸ“‘ routing 2", block.Header.Height)
   519  		league.receiveBlock(block)
   520  		//fmt.Println("πŸ”† πŸ“‘ routing 3", this.isminer, this.lighthouse)
   521  		if !this.isminer && !this.lighthouse {
   522  			//fmt.Println("πŸ”† πŸ“‘ routing 4 registerConsume")
   523  			//league.registerConsume(this.adpter, this.setStopSignal, this.getLightHouse)
   524  			go func(league *LeagueConsumer) {
   525  				league.registerConsume(this.setStopSignal, this.getLightHouse)
   526  			}(league)
   527  		}
   528  	}
   529  }
   530  
   531  func (this *LeagueConsumers) getLeagueConsume(leagueId common.Address) *LeagueConsumer {
   532  	this.m.Lock()
   533  	defer this.m.Unlock()
   534  	{
   535  		if this.consumers[leagueId] == nil {
   536  			this.consumers[leagueId] = &LeagueConsumer{
   537  				blockPool:   make(map[uint64]*orgtypes.Block),
   538  				receiveSign: make(chan uint64, 100),
   539  				leagueId:    leagueId,
   540  				mainTxUsed:  make(common.HashArray, 0, 50),
   541  				mainTxsNew:  make(map[uint64]maintypes.Transactions),
   542  				adapter:     this.adpter,
   543  			}
   544  			//fmt.Println("blockHash len:", len(this.consumers[leagueId].blockHashes))
   545  			this.leagueSize++
   546  		}
   547  		return this.consumers[leagueId]
   548  	}
   549  }
   550  
   551  /*func (this *LeagueConsumers) checkMainTx(tx *maintypes.Transaction) error {
   552  	if tx.TxType != maintypes.ConsensusLeague || tx.TxType != maintypes.LockLeague {
   553  		return nil
   554  	}
   555  	if tx.TxData.StartHeight == 1 {
   556  		if this.consumers[tx.TxData.LeagueId] != nil {
   557  			return fmt.Errorf("maintx for league check not match.")
   558  		}
   559  		l := tx.TxData.EndHeight - tx.TxData.StartHeight
   560  		asses := make([]storelaw.AccountStaters, 0, l)
   561  		blocks := make([]*orgtypes.Block, l)
   562  		for i := tx.TxData.StartHeight; i <= tx.TxData.EndHeight; i++ {
   563  			block := this.consumers[tx.TxData.LeagueId].blockPool[i]
   564  			if block == nil {
   565  				return fmt.Errorf("maintx for league check not match.")
   566  			}
   567  			ass, _, err := this.consumers[tx.TxData.LeagueId].verifyBlock(block, this.adpter)
   568  			if err != nil {
   569  				return err
   570  			}
   571  			asses = append(asses, ass)
   572  			blocks = append(blocks, block)
   573  		}
   574  
   575  	}
   576  	return nil
   577  }
   578  */