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

     1  package mainchain
     2  
     3  import (
     4  	"math/big"
     5  	"sync"
     6  	"time"
     7  
     8  	"fmt"
     9  
    10  	"log"
    11  
    12  	"github.com/ahmetb/go-linq"
    13  	//"github.com/vechain/thor/block"
    14  	"github.com/sixexorg/magnetic-ring/common"
    15  	maintypes "github.com/sixexorg/magnetic-ring/core/mainchain/types"
    16  	orgtypes "github.com/sixexorg/magnetic-ring/core/orgchain/types"
    17  	"github.com/sixexorg/magnetic-ring/crypto"
    18  	"github.com/sixexorg/magnetic-ring/errors"
    19  	"github.com/sixexorg/magnetic-ring/store/mainchain/extstates"
    20  	"github.com/sixexorg/magnetic-ring/store/orgchain/genesis"
    21  	"github.com/sixexorg/magnetic-ring/store/storelaw"
    22  )
    23  
    24  type LeagueConsumer struct {
    25  	m        sync.RWMutex
    26  	pubkey   crypto.PublicKey //init from genesis block
    27  	leagueId common.Address
    28  	//currentBlock *orgtypes.Block                   //the block in analysis
    29  	currBlockHash common.Hash                                                               //1
    30  	curHeight     uint64                                                                    //1
    31  	lockerr       error                             // for league lock  and this signal signal indicates that the consumer will not validate new blocks
    32  	lastHeight    uint64                            //lastheight change when determine how much height to cut
    33  	blockPool     map[uint64]*orgtypes.Block        //the receive block into here and remove the map key which less than lastHeight
    34  	mainTxUsed    common.HashArray                  //Cache the main chain block reference of the current compute block,it's persisted to the database at the end
    35  	mainTxsNew    map[uint64]maintypes.Transactions //newly produced main chain tx,Delete after generateMainTx(func) or checkLeagueResult(func)
    36  	receiveSign   chan uint64                       //notice end
    37  	adapter       *ConsumerAdapter
    38  	willdo        uint64
    39  	//needRemove    []uint64
    40  	executing bool
    41  }
    42  
    43  //receiveBlock is shunt the block to its corresponding location, which requires special handling if it is a genesis block
    44  //func (this *LeagueConsumer) receiveBlock(block *orgtypes.Block, teller Teller, initTeller InitTellerfunc) {
    45  func (this *LeagueConsumer) receiveBlock(block *orgtypes.Block) {
    46  	if block.Header.Height <= this.currentHeight() {
    47  		return
    48  	}
    49  	if block.Header.Height == 1 {
    50  		if this.pubkey != nil {
    51  			return
    52  		}
    53  		//todo Verify the circle tx of the public chain and initialize the creation data
    54  	} else {
    55  		if !this.authenticate(block) {
    56  			return
    57  		}
    58  	}
    59  	fmt.Println("πŸ“‘ receiveBlock")
    60  	this.fillBlockPool(block)
    61  }
    62  func (this *LeagueConsumer) commitLastHeight() {
    63  	if this.willdo != 0 {
    64  		willdo := this.willdo
    65  		last := this.getLastHeight()
    66  		this.setLastHeight(willdo)
    67  		this.removeMainNewBefore(last, willdo)
    68  		fmt.Println("πŸ“‘ commitLastHeight ", last, willdo)
    69  	}
    70  }
    71  func (this *LeagueConsumer) rollbackWillDo() {
    72  	this.m.Lock()
    73  	defer this.m.Unlock()
    74  	this.willdo = 0
    75  }
    76  
    77  func (this *LeagueConsumer) generateMainTx(right uint64) (mtc *MainTxCradle, err error) {
    78  	if this.getLastHeight() >= right {
    79  		return nil, errors.ERR_RADAR_NOT_FOUNT
    80  	}
    81  	if this.currentHeight() >= right {
    82  		return this.generateMainTxPart1(this.getLastHeight()+1, right)
    83  	}
    84  	return nil, errors.ERR_RADAR_NOT_FOUNT
    85  	/*	if this.currentHeight() < right {
    86  			if this.lockerr != nil {
    87  				//TODO Lock circle
    88  				return this.generateMainTxPart1(this.getLastHeight()+1, this.currentHeight(), maintypes.LockLeague)
    89  
    90  			} else {
    91  				return nil, errors.ERR_RADAR_M_HEIGHT_BLOCKING
    92  			}
    93  		} else {
    94  			return this.generateMainTxPart1(this.getLastHeight()+1, right, maintypes.ConsensusLeague)
    95  		}*/
    96  }
    97  func (this *LeagueConsumer) generateMainTxPart1(left, right uint64) (*MainTxCradle, error) {
    98  	hashes, gasSum, err := this.adapter.Ledger.GetBlockHashSpan(this.leagueId, left, right)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	fmt.Printf("πŸ“‘ πŸ”­  generateMainTxPart1 left:%d right:%d last:%d hashes:%v \n", left, right, this.getLastHeight(), hashes)
   103  	tp := maintypes.ConsensusLeague
   104  	if this.lockerr != nil {
   105  		if this.currentHeight() == right {
   106  			tp = maintypes.LockLeague
   107  		}
   108  	}
   109  
   110  	mtx := &maintypes.Transaction{
   111  		Version: maintypes.MainTxVersion,
   112  		TxType:  tp,
   113  		TxData: &maintypes.TxData{
   114  			StartHeight: this.getLastHeight() + 1,
   115  			EndHeight:   right,
   116  			BlockRoot:   hashes.GetHashRoot(),
   117  			LeagueId:    this.leagueId,
   118  			Fee:         gasSum,
   119  		},
   120  	}
   121  	if tp == maintypes.LockLeague {
   122  		mtx.TxData.UnlockEnergy = big.NewInt(10000)
   123  	}
   124  	txCount := 0
   125  	for i := left; i <= right; i++ {
   126  		txCount += this.mainTxsNew[i].Len()
   127  	}
   128  	referenceTxs := make(maintypes.Transactions, 0, txCount)
   129  	//removeK := make([]uint64, 0, right-left+1)
   130  	for i := left; i <= right; i++ {
   131  		referenceTxs = append(referenceTxs, this.mainTxsNew[i]...)
   132  	}
   133  	mtc := &MainTxCradle{
   134  		LeagueId:     this.leagueId,
   135  		Check:        mtx,
   136  		ReferenceTxs: referenceTxs,
   137  	}
   138  	//this.needRemove = removeK
   139  	this.willdo = right
   140  	//TODO update
   141  	//this.setLastHeight(right)
   142  	//this.removeMainNewBefore(removeK)
   143  	return mtc, nil
   144  }
   145  
   146  //RegisterConsume is controls the life cycle of a single round operation
   147  func (this *LeagueConsumer) registerConsume(setStopSignal func(leagueId common.Address, tx *maintypes.Transaction), getLightHouse func() bool) {
   148  	if this.executing {
   149  		return
   150  	}
   151  	this.start()
   152  	go func() {
   153  		//TODO FOR TEST
   154  		//this.clearHistorical()
   155  		for height := range this.receiveSign {
   156  			if height == this.currentHeight()+1 {
   157  				err := this.consume()
   158  				if err != nil {
   159  					this.lockerr = err
   160  					return
   161  				}
   162  			} else if height == 0 {
   163  				tx, err := this.createMainChainTx()
   164  				if err != nil {
   165  					//todo log
   166  					log.Println("func registerConsume createMainChainTx err ", err.Error())
   167  				}
   168  				setStopSignal(this.leagueId, tx)
   169  				this.stop()
   170  				return
   171  			}
   172  		}
   173  	}()
   174  
   175  	go func() {
   176  		//todo This is the first use here. If you need to optimize the stop later, you can optimize it globally. Otherwise, this poll will increase in disguise.
   177  		for {
   178  			if getLightHouse() {
   179  				this.receiveSign <- 0
   180  			}
   181  			time.Sleep(time.Second)
   182  		}
   183  	}()
   184  }
   185  
   186  func (this *LeagueConsumer) consume() error {
   187  	//fmt.Println("πŸ”† πŸ“‘ consume 0 start block height")
   188  	block := this.findBlockPool(this.currentHeight() + 1)
   189  	if block != nil {
   190  		fmt.Println("πŸ”† πŸ“‘ consume 1 start block height", block.Header.Height)
   191  		blkInfo, err := this.verifyBlock(block)
   192  		if err != nil {
   193  			fmt.Println("πŸ”† πŸ“‘ consume 2 lock", block.Header.Height, ",err:", err)
   194  			//log.Println("func consume err ", err.Error())
   195  			/*if block.Header.Height == 1 {
   196  				return
   197  			}*/
   198  			return err
   199  		}
   200  		fmt.Println("πŸ”† πŸ“‘ consume 2.5 saveall", blkInfo.Block.Header.Height)
   201  		//todo Confirm tx generation, necessary data storage
   202  		err = this.saveAll(blkInfo)
   203  		fmt.Println("πŸ”† πŸ“‘ consume 3 saveall", err)
   204  		if err != nil {
   205  			//log.Println("func consume err ", err.Error())
   206  			return err
   207  		}
   208  		//this.blockHashes = append(this.blockHashes, block.Hash())
   209  		this.setCurrentBlock(block)
   210  		fmt.Println("πŸ”† πŸ“‘ consume 4 ok")
   211  	}
   212  	return nil
   213  }
   214  
   215  //VerifyBlock is verify the block and compute all the details, which must be exactly the same as the circle validation
   216  func (this *LeagueConsumer) verifyBlock(block *orgtypes.Block) (*storelaw.OrgBlockInfo, error) {
   217  	fmt.Println("🚫 verifyBlock bonus ", block.Header.Bonus)
   218  	if this.currentHeight()+1 != block.Header.Height {
   219  		return nil, errors.ERR_EXTERNAL_HEIGHT_NOT_MATCH
   220  	}
   221  	if this.currentBlockHash() != block.Header.PrevBlockHash {
   222  		return nil, fmt.Errorf("%s,the real prevHash is %s ,the wrong is %s",
   223  			errors.ERR_EXTERNAL_PREVBLOCKHASH_DIFF,
   224  			this.currentBlockHash().String(),
   225  			block.Header.PrevBlockHash.String())
   226  	}
   227  	//todo more detailed description of the error
   228  	if block.Header.Height == 1 {
   229  		tx, height, err := this.adapter.FuncGenesis(block.Header.LeagueId)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  
   234  		blockRef, err := this.adapter.FuncBlk(height)
   235  		if err != nil {
   236  			return nil, err
   237  		}
   238  
   239  		blockGenesis, sts := genesis.GensisBlock(tx, blockRef.Timestamp)
   240  		this.leagueId = block.Header.LeagueId
   241  		stsExt := make(storelaw.AccountStaters, 1)
   242  		ut := big.NewInt(0)
   243  		for _, v := range sts {
   244  			stsExt[0] = &extstates.LeagueAccountState{
   245  				Address:  v.Account(),
   246  				LeagueId: block.Header.LeagueId,
   247  				Height:   block.Header.Height,
   248  				Data: &extstates.Account{
   249  					Nonce:       v.Nonce(),
   250  					Balance:     v.Balance(),
   251  					EnergyBalance: v.Energy(),
   252  					BonusHeight: 0,
   253  				},
   254  			}
   255  			ut.Add(ut, v.Balance())
   256  		}
   257  
   258  		/*blockGenesis.Header.Coinbase = block.Header.Coinbase
   259  		blockGenesis.Header.Extra = block.Header.Extra*/
   260  
   261  		blkInfo := &storelaw.OrgBlockInfo{
   262  			Block:     blockGenesis,
   263  			AccStates: stsExt,
   264  			UT:        ut,
   265  			FeeSum:    big.NewInt(0),
   266  		}
   267  		return blkInfo, nil
   268  	} else {
   269  		var errCheck error
   270  		this.initializeMainTxHeight(block.Header.Height, block.Transactions.Len()/5)
   271  		for _, v := range block.Transactions {
   272  			errCheck = this.checkReferencedTransaction(v)
   273  			if errCheck != nil {
   274  				return nil, errCheck
   275  			}
   276  			if v.TxType != orgtypes.VoteIncreaseUT {
   277  				this.orgTxBirthMainTx(v, block.Header.Height, block.Header.LeagueId, nil)
   278  			}
   279  		}
   280  		blkInfo, err := this.adapter.FuncValidate(block, this.adapter.Ledger)
   281  
   282  		fmt.Printf("🚫 cmp β­• \n"+
   283  			"πŸš«β“ version %v %v \n"+
   284  			"πŸš«β“ prevHash %s %s \n"+
   285  			"πŸš«β“ leagueId %s %s \n"+
   286  			"πŸš«β“ txRoot   %s %s \n"+
   287  			"πŸš«β“ stateRoot %s %s \n"+
   288  			"πŸš«β“ receipt %s %s \n"+
   289  			"πŸš«β“ timestamp %d %d \n"+
   290  			"πŸš«β“ height %d %d \n"+
   291  			"πŸš«β“ difficulty %d %d \n"+
   292  			"πŸš«β“ coinbase %s %s \n"+
   293  			"πŸš«β“ extra %s %s\n"+
   294  			"πŸš«β“ hash %s %s \n",
   295  			blkInfo.Block.Header.Version, block.Header.Version,
   296  			blkInfo.Block.Header.PrevBlockHash.String(), block.Header.PrevBlockHash.String(),
   297  			blkInfo.Block.Header.LeagueId.ToString(), block.Header.LeagueId.ToString(),
   298  			blkInfo.Block.Header.TxRoot.String(), block.Header.TxRoot.String(),
   299  			blkInfo.Block.Header.StateRoot.String(), block.Header.StateRoot.String(),
   300  			blkInfo.Block.Header.ReceiptsRoot.String(), block.Header.ReceiptsRoot.String(),
   301  			blkInfo.Block.Header.Timestamp, block.Header.Timestamp,
   302  			blkInfo.Block.Header.Height, block.Header.Height,
   303  			blkInfo.Block.Header.Difficulty.Uint64(), block.Header.Difficulty.Uint64(),
   304  			blkInfo.Block.Header.Coinbase.ToString(), block.Header.Coinbase.ToString(),
   305  			string(blkInfo.Block.Header.Extra), string(block.Header.Extra),
   306  			blkInfo.Block.Hash().String(), block.Hash().String(),
   307  		)
   308  		if err != nil {
   309  			return nil, err
   310  		}
   311  		fmt.Println("🚫  voteFirstPass num ", blkInfo.VoteFirstPass.Len())
   312  		for _, v := range blkInfo.VoteFirstPass {
   313  			ff := func(leagueId common.Address) (rate uint32) {
   314  				tx, _, _ := this.adapter.Ledger.LightLedger.GetTxByLeagueId(leagueId)
   315  				return tx.TxData.Rate
   316  			}
   317  			this.orgTxBirthMainTx(v, block.Header.Height, block.Header.LeagueId, ff)
   318  
   319  		}
   320  		return blkInfo, nil
   321  	}
   322  }
   323  func (this *LeagueConsumer) setBlockCurrentBySync(hash common.Hash, height uint64) {
   324  	this.m.Lock()
   325  	defer this.m.Unlock()
   326  	if this.curHeight+1 == height {
   327  		this.curHeight = height
   328  		this.currBlockHash = hash
   329  	}
   330  }
   331  
   332  func (this *LeagueConsumer) saveAll(blkInfo *storelaw.OrgBlockInfo) error {
   333  	err := this.adapter.Ledger.SaveAll(blkInfo, this.mainTxUsed)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	this.mainTxUsed = make(common.HashArray, 0, 50)
   338  	this.clearBlockPool(blkInfo.Block.Header.Height)
   339  	return nil
   340  }
   341  
   342  func (this *LeagueConsumer) createMainChainTx() (*maintypes.Transaction, error) {
   343  	var (
   344  		tx  *maintypes.Transaction
   345  		err error = nil
   346  	)
   347  	left := this.getLastHeight()
   348  	right := this.currentHeight()
   349  	if right > left {
   350  		hashes, gasSum, err := this.adapter.Ledger.GetBlockHashSpan(this.leagueId, left+1, right)
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  		//log.Println("createMainChainTx func: offset=>", l)
   355  		txdata := &maintypes.TxData{
   356  			LeagueId:    this.leagueId,
   357  			StartHeight: left + 1,
   358  			EndHeight:   right,
   359  			BlockRoot:   hashes.GetHashRoot(),
   360  			Energy:        gasSum,
   361  		}
   362  		if this.lockerr != nil {
   363  			txdata.UnlockEnergy = unlockEnergy
   364  			txdata.Msg = common.Hash{} //TODO
   365  			tx, err = maintypes.NewTransaction(maintypes.LockLeague, 0x01, txdata)
   366  
   367  		} else {
   368  			tx, err = maintypes.NewTransaction(maintypes.ConsensusLeague, 0x01, txdata)
   369  		}
   370  	}
   371  	return tx, err
   372  }
   373  
   374  func (this *LeagueConsumer) checkGenesisBlock(block *orgtypes.Block) {
   375  	if this.currentHeight() != 1 {
   376  		return
   377  	}
   378  	//this.pubkey = &ecdsa.PrivateKey{}
   379  	//TODO get tx from mainchain
   380  
   381  }
   382  
   383  //checkReferencedTransaction is only check the transaction from the main chain to the league
   384  func (this *LeagueConsumer) checkReferencedTransaction(tx *orgtypes.Transaction) error {
   385  	if containMainType(tx.TxType) {
   386  
   387  		if this.contains(tx.TxData.TxHash) {
   388  			return errors.ERR_EXTERNAL_MAIN_TX_USED
   389  		}
   390  		bl := this.adapter.Ledger.MainTxUsedExist(tx.Hash())
   391  		if bl {
   392  			return errors.ERR_EXTERNAL_MAIN_TX_USED
   393  		}
   394  		this.mainTxUsed = append(this.mainTxUsed, tx.TxData.TxHash)
   395  		txMain, _, err := this.adapter.FuncTx(tx.TxData.TxHash)
   396  		if err != nil {
   397  			return err
   398  		}
   399  
   400  		err = checkOrgTx(txMain, tx)
   401  		return err
   402  		/*//todo need receipt
   403  		if tx.TxType == orgtypes.Join &&
   404  			txMain.TxType == maintypes.JoinLeague &&
   405  			txMain.TxData.From == tx.TxData.From {
   406  			return nil
   407  
   408  		} else if tx.TxType == orgtypes.EnergyFromMain &&
   409  			txMain.TxType == maintypes.EnergyToLeague &&
   410  			txMain.TxData.From == tx.TxData.From &&
   411  			txMain.TxData.Energy.Cmp(tx.TxData.Energy) == 0 {
   412  			return nil
   413  		}
   414  		return errors.ERR_EXTERNAL_TX_REFERENCE_WRONG*/
   415  	}
   416  	return nil
   417  }
   418  func (this *LeagueConsumer) contains(txHash common.Hash) bool {
   419  	return linq.From(this.mainTxUsed).Contains(txHash)
   420  }
   421  
   422  //TODO Verify that the block is the one that the circle itself gave.
   423  func (this *LeagueConsumer) authenticate(block *orgtypes.Block) bool {
   424  	return true
   425  }
   426  
   427  func (this *LeagueConsumer) setCurrentBlock(block *orgtypes.Block) {
   428  	this.m.Lock()
   429  	defer this.m.Unlock()
   430  	this.currBlockHash = block.Hash()
   431  	this.curHeight = block.Header.Height
   432  	fmt.Println("🚫 setCurrentBlock height ", block.Header.Height)
   433  	/*this.adapter.initTeller()
   434  	this.adapter.P2pTlr.Tell(&common.NodeLH{
   435  		NodeId:   this.adapter.NodeId,
   436  		LeagueId: this.leagueId,
   437  		Height:   block.Header.Height,
   438  	})*/
   439  	//fmt.Printf("🚫 πŸ“«  send nodeLH nodeId:%s leagueId:%s height:%d\n", this.adapter.NodeId, this.leagueId.ToString(), block.Header.Height)
   440  }
   441  func (this *LeagueConsumer) currentHeight() uint64 {
   442  	this.m.RLock()
   443  	defer this.m.RUnlock()
   444  	return this.curHeight
   445  }
   446  func (this *LeagueConsumer) setLastHeight(height uint64) {
   447  	this.m.Lock()
   448  	defer this.m.Unlock()
   449  	this.lastHeight = height
   450  	this.willdo = 0
   451  }
   452  func (this *LeagueConsumer) getLastHeight() uint64 {
   453  	this.m.RLock()
   454  	defer this.m.RUnlock()
   455  	return this.lastHeight
   456  }
   457  
   458  func (this *LeagueConsumer) currentBlockHash() common.Hash {
   459  	this.m.RLock()
   460  	defer this.m.RUnlock()
   461  	return this.currBlockHash
   462  }
   463  func (this *LeagueConsumer) UnlockLeague() {
   464  	if this.lockerr != nil {
   465  		this.lockerr = nil
   466  	}
   467  }
   468  
   469  func (this *LeagueConsumer) start() {
   470  	this.executing = true
   471  }
   472  func (this *LeagueConsumer) stop() {
   473  	this.executing = false
   474  }
   475  func containMainType(key orgtypes.TransactionType) bool {
   476  	_, ok := txTypeForReference[key]
   477  	return ok
   478  }
   479  
   480  func containTransferToMainTx(key orgtypes.TransactionType) bool {
   481  	_, ok := txTypeForTransfer[key]
   482  	return ok
   483  }
   484  func (this *LeagueConsumer) initializeMainTxHeight(height uint64, cap int) {
   485  	this.mainTxsNew[height] = make(maintypes.Transactions, 0, cap)
   486  }
   487  func (this *LeagueConsumer) orgTxBirthMainTx(tx *orgtypes.Transaction, height uint64, leagueId common.Address, f func(leagueId common.Address) (rate uint32)) {
   488  	mainTx := orgTxBirthMainTx(tx, height, leagueId, f)
   489  	if mainTx != nil {
   490  		fmt.Println("🚫 orgTxBirthMainTx append txType ", mainTx.TxType, "height ", height)
   491  		this.appendMainNew(mainTx, height)
   492  	}
   493  }
   494  func (this *LeagueConsumer) appendMainNew(mainTx *maintypes.Transaction, height uint64) {
   495  	this.m.Lock()
   496  	defer this.m.Unlock()
   497  	this.mainTxsNew[height] = append(this.mainTxsNew[height], mainTx)
   498  }
   499  func (this *LeagueConsumer) removeMainNewBefore(start, end uint64) {
   500  	this.m.Lock()
   501  	defer this.m.Unlock()
   502  	for i := start; i <= end; i++ {
   503  		delete(this.mainTxsNew, i)
   504  	}
   505  }
   506  func (this *LeagueConsumer) fillBlockPool(block *orgtypes.Block) {
   507  	this.m.Lock()
   508  	defer this.m.Unlock()
   509  	if this.blockPool[block.Header.Height] == nil {
   510  		this.blockPool[block.Header.Height] = block
   511  		/*opd := &p2pcommon.OrgPendingData{
   512  			BANodeSrc: false,
   513  			OrgId:     block.Header.LeagueId,
   514  			Block:     block,
   515  		}*/
   516  		this.receiveSign <- block.Header.Height
   517  		/*	this.adapter.initTeller()
   518  			this.adapter.P2pTlr.Tell(opd)*/
   519  
   520  		/*	actor, err := bactor.GetActorPid(bactor.P2PACTOR)
   521  			if err != nil {
   522  				panic(err)
   523  			}
   524  			actor.Tell(opd)*/
   525  	}
   526  }
   527  func (this *LeagueConsumer) findBlockPool(height uint64) *orgtypes.Block {
   528  	this.m.RLock()
   529  	defer this.m.RUnlock()
   530  	return this.blockPool[height]
   531  }
   532  func (this *LeagueConsumer) clearBlockPool(targetHeight uint64) {
   533  	this.m.Lock()
   534  	defer this.m.Unlock()
   535  	for k, _ := range this.blockPool {
   536  		if k <= targetHeight {
   537  			delete(this.blockPool, k)
   538  		}
   539  	}
   540  }