github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/core/chains_info.go (about)

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/intfoundation/go-crypto"
     7  	dbm "github.com/intfoundation/go-db"
     8  	"github.com/intfoundation/go-wire"
     9  	"github.com/intfoundation/intchain/common"
    10  	"github.com/intfoundation/intchain/common/math"
    11  	ep "github.com/intfoundation/intchain/consensus/ipbft/epoch"
    12  	"github.com/intfoundation/intchain/core/state"
    13  	"github.com/intfoundation/intchain/log"
    14  	"math/big"
    15  	"os"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  const (
    21  	OFFICIAL_MINIMUM_VALIDATORS = 1
    22  	OFFICIAL_MINIMUM_DEPOSIT    = "100000000000000000000000" // 100,000 * e18
    23  )
    24  
    25  type CoreChainInfo struct {
    26  	db dbm.DB
    27  
    28  	// Common Info
    29  	Owner   common.Address
    30  	ChainId string
    31  
    32  	// Setup Info
    33  	MinValidators    uint16
    34  	MinDepositAmount *big.Int
    35  	StartBlock       *big.Int
    36  	EndBlock         *big.Int
    37  
    38  	//joined - during creation phase
    39  	JoinedValidators []JoinedValidator
    40  
    41  	//validators - for stable phase; should be Epoch information
    42  	EpochNumber uint64
    43  
    44  	//the statitics for balance in & out
    45  	//depositInMainChain >= depositInChildChain
    46  	//withdrawFromChildChain >= withdrawFromMainChain
    47  	//depositInMainChain >= withdrawFromChildChain
    48  	DepositInMainChain     *big.Int //total deposit by users from main
    49  	DepositInChildChain    *big.Int //total deposit allocated to users in child chain
    50  	WithdrawFromChildChain *big.Int //total withdraw by users from child chain
    51  	WithdrawFromMainChain  *big.Int //total withdraw refund to users in main chain
    52  }
    53  
    54  type JoinedValidator struct {
    55  	PubKey        crypto.PubKey
    56  	Address       common.Address
    57  	DepositAmount *big.Int
    58  }
    59  
    60  type ChainInfo struct {
    61  	CoreChainInfo
    62  
    63  	//be careful, this Epoch could be different with the current epoch in the child chain
    64  	//it is just for cache
    65  	Epoch *ep.Epoch
    66  }
    67  
    68  const (
    69  	chainInfoKey  = "CHAIN"
    70  	ethGenesisKey = "ETH_GENESIS"
    71  	tdmGenesisKey = "TDM_GENESIS"
    72  )
    73  
    74  var allChainKey = []byte("AllChainID")
    75  
    76  const specialSep = ";"
    77  
    78  var mtx sync.RWMutex
    79  
    80  func calcCoreChainInfoKey(chainId string) []byte {
    81  	return []byte(chainInfoKey + ":" + chainId)
    82  }
    83  
    84  func calcEpochKey(number uint64, chainId string) []byte {
    85  	return []byte(chainInfoKey + fmt.Sprintf("-%v-%s", number, chainId))
    86  }
    87  
    88  func calcETHGenesisKey(chainId string) []byte {
    89  	return []byte(ethGenesisKey + ":" + chainId)
    90  }
    91  
    92  func calcTDMGenesisKey(chainId string) []byte {
    93  	return []byte(tdmGenesisKey + ":" + chainId)
    94  }
    95  
    96  func GetChainInfo(db dbm.DB, chainId string) *ChainInfo {
    97  	mtx.RLock()
    98  	defer mtx.RUnlock()
    99  
   100  	cci := loadCoreChainInfo(db, chainId)
   101  	if cci == nil {
   102  		return nil
   103  	}
   104  
   105  	ci := &ChainInfo{
   106  		CoreChainInfo: *cci,
   107  	}
   108  
   109  	epoch := loadEpoch(db, cci.EpochNumber, chainId)
   110  	if epoch != nil {
   111  		ci.Epoch = epoch
   112  	}
   113  
   114  	log.Debugf("LoadChainInfo(), chainInfo is: %v\n", ci)
   115  
   116  	return ci
   117  }
   118  
   119  func SaveChainInfo(db dbm.DB, ci *ChainInfo) error {
   120  	mtx.Lock()
   121  	defer mtx.Unlock()
   122  
   123  	log.Debugf("ChainInfo Save(), info is: (%v)\n", ci)
   124  
   125  	err := saveCoreChainInfo(db, &ci.CoreChainInfo)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	if ci.Epoch != nil {
   131  		err = saveEpoch(db, ci.Epoch, ci.ChainId)
   132  		if err != nil {
   133  			return err
   134  		}
   135  	}
   136  
   137  	saveId(db, ci.ChainId)
   138  
   139  	return nil
   140  }
   141  
   142  func SaveFutureEpoch(db dbm.DB, futureEpoch *ep.Epoch, chainId string) error {
   143  	mtx.Lock()
   144  	defer mtx.Unlock()
   145  
   146  	if futureEpoch != nil {
   147  		err := saveEpoch(db, futureEpoch, chainId)
   148  		if err != nil {
   149  			return err
   150  		}
   151  	}
   152  	return nil
   153  }
   154  
   155  func loadCoreChainInfo(db dbm.DB, chainId string) *CoreChainInfo {
   156  
   157  	cci := CoreChainInfo{db: db}
   158  	buf := db.Get(calcCoreChainInfoKey(chainId))
   159  	if len(buf) == 0 {
   160  		return nil
   161  	} else {
   162  		r, n, err := bytes.NewReader(buf), new(int), new(error)
   163  		wire.ReadBinaryPtr(&cci, r, 0, n, err)
   164  		if *err != nil {
   165  			// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
   166  			log.Debugf("LoadChainInfo: Data has been corrupted or its spec has changed: %v", *err)
   167  			os.Exit(1)
   168  		}
   169  	}
   170  	return &cci
   171  }
   172  
   173  func saveCoreChainInfo(db dbm.DB, cci *CoreChainInfo) error {
   174  
   175  	db.SetSync(calcCoreChainInfoKey(cci.ChainId), wire.BinaryBytes(*cci))
   176  	return nil
   177  }
   178  
   179  func (cci *CoreChainInfo) TotalDeposit() *big.Int {
   180  	sum := big.NewInt(0)
   181  	for _, v := range cci.JoinedValidators {
   182  		sum.Add(sum, v.DepositAmount)
   183  	}
   184  	return sum
   185  }
   186  
   187  func loadEpoch(db dbm.DB, number uint64, chainId string) *ep.Epoch {
   188  	epochBytes := db.Get(calcEpochKey(number, chainId))
   189  	return ep.FromBytes(epochBytes)
   190  }
   191  
   192  func saveEpoch(db dbm.DB, epoch *ep.Epoch, chainId string) error {
   193  
   194  	db.SetSync(calcEpochKey(epoch.Number, chainId), epoch.Bytes())
   195  	return nil
   196  }
   197  
   198  func (ci *ChainInfo) GetEpochByBlockNumber(blockNumber uint64) *ep.Epoch {
   199  	mtx.RLock()
   200  	defer mtx.RUnlock()
   201  
   202  	if blockNumber < 0 {
   203  		return ci.Epoch
   204  	} else {
   205  		epoch := ci.Epoch
   206  		if epoch == nil {
   207  			return nil
   208  		}
   209  		if blockNumber >= epoch.StartBlock && blockNumber <= epoch.EndBlock {
   210  			return epoch
   211  		}
   212  
   213  		number := epoch.Number
   214  		// If blockNumber > epoch EndBlock, find future epoch
   215  		if blockNumber > epoch.EndBlock {
   216  			for {
   217  				number++
   218  
   219  				ep := loadEpoch(ci.db, number, ci.ChainId)
   220  				if ep == nil {
   221  					return nil
   222  				}
   223  
   224  				if blockNumber >= ep.StartBlock && blockNumber <= ep.EndBlock {
   225  					return ep
   226  				}
   227  			}
   228  
   229  		} else { // If blockNumber < epoch StartBlock, find history epoch
   230  			for {
   231  				if number == 0 {
   232  					break
   233  				}
   234  				number--
   235  
   236  				ep := loadEpoch(ci.db, number, ci.ChainId)
   237  				if ep == nil {
   238  					return nil
   239  				}
   240  
   241  				if blockNumber >= ep.StartBlock && blockNumber <= ep.EndBlock {
   242  					return ep
   243  				}
   244  			}
   245  		}
   246  	}
   247  	return nil
   248  }
   249  
   250  func saveId(db dbm.DB, chainId string) {
   251  
   252  	buf := db.Get(allChainKey)
   253  
   254  	if len(buf) == 0 {
   255  		db.SetSync(allChainKey, []byte(chainId))
   256  		log.Debugf("ChainInfo SaveId(), chainId is: %s", chainId)
   257  	} else {
   258  
   259  		strIdArr := strings.Split(string(buf), specialSep)
   260  
   261  		found := false
   262  		for _, id := range strIdArr {
   263  			if id == chainId {
   264  				found = true
   265  				break
   266  			}
   267  		}
   268  
   269  		if !found {
   270  			strIdArr = append(strIdArr, chainId)
   271  			strIds := strings.Join(strIdArr, specialSep)
   272  			db.SetSync(allChainKey, []byte(strIds))
   273  
   274  			log.Debugf("ChainInfo SaveId(), strIds is: %s", strIds)
   275  		}
   276  	}
   277  }
   278  
   279  func GetChildChainIds(db dbm.DB) []string {
   280  	mtx.RLock()
   281  	defer mtx.RUnlock()
   282  
   283  	buf := db.Get(allChainKey)
   284  
   285  	log.Debugf("Get child chain IDs, buf is %v, len is %d", buf, len(buf))
   286  
   287  	if len(buf) == 0 {
   288  		return []string{}
   289  	}
   290  
   291  	return strings.Split(string(buf), specialSep)
   292  }
   293  
   294  func CheckChildChainRunning(db dbm.DB, chainId string) bool {
   295  	ids := GetChildChainIds(db)
   296  
   297  	for _, id := range ids {
   298  		if id == chainId {
   299  			return true
   300  		}
   301  	}
   302  
   303  	return false
   304  }
   305  
   306  // SaveChainGenesis save the genesis file for child chain
   307  func SaveChainGenesis(db dbm.DB, chainId string, ethGenesis, tdmGenesis []byte) {
   308  	mtx.Lock()
   309  	defer mtx.Unlock()
   310  
   311  	// Save the intprotocol genesis
   312  	db.SetSync(calcETHGenesisKey(chainId), ethGenesis)
   313  
   314  	// Save the tdm genesis
   315  	db.SetSync(calcTDMGenesisKey(chainId), tdmGenesis)
   316  }
   317  
   318  // LoadChainGenesis load the genesis file for child chain
   319  func LoadChainGenesis(db dbm.DB, chainId string) (ethGenesis, tdmGenesis []byte) {
   320  	mtx.RLock()
   321  	defer mtx.RUnlock()
   322  
   323  	ethGenesis = db.Get(calcETHGenesisKey(chainId))
   324  	tdmGenesis = db.Get(calcTDMGenesisKey(chainId))
   325  	return
   326  }
   327  
   328  // ---------------------
   329  // Pending Chain
   330  var pendingChainMtx sync.Mutex
   331  
   332  var pendingChainIndexKey = []byte("PENDING_CHAIN_IDX")
   333  
   334  func calcPendingChainInfoKey(chainId string) []byte {
   335  	return []byte("PENDING_CHAIN:" + chainId)
   336  }
   337  
   338  type pendingIdxData struct {
   339  	ChainID string
   340  	Start   *big.Int
   341  	End     *big.Int
   342  }
   343  
   344  // GetPendingChildChainData get the pending child chain data from db with key pending chain
   345  func GetPendingChildChainData(db dbm.DB, chainId string) *CoreChainInfo {
   346  
   347  	pendingChainByteSlice := db.Get(calcPendingChainInfoKey(chainId))
   348  	if pendingChainByteSlice != nil {
   349  		var cci CoreChainInfo
   350  		wire.ReadBinaryBytes(pendingChainByteSlice, &cci)
   351  		return &cci
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  // CreatePendingChildChainData create the pending child chain data with index
   358  func CreatePendingChildChainData(db dbm.DB, cci *CoreChainInfo) {
   359  	storePendingChildChainData(db, cci, true)
   360  }
   361  
   362  // UpdatePendingChildChainData update the pending child chain data without index
   363  func UpdatePendingChildChainData(db dbm.DB, cci *CoreChainInfo) {
   364  	storePendingChildChainData(db, cci, false)
   365  }
   366  
   367  // storePendingChildChainData save the pending child chain data into db with key pending chain
   368  func storePendingChildChainData(db dbm.DB, cci *CoreChainInfo, create bool) {
   369  	pendingChainMtx.Lock()
   370  	defer pendingChainMtx.Unlock()
   371  
   372  	// store the data
   373  	db.SetSync(calcPendingChainInfoKey(cci.ChainId), wire.BinaryBytes(*cci))
   374  
   375  	if create {
   376  		// index the data
   377  		var idx []pendingIdxData
   378  		pendingIdxByteSlice := db.Get(pendingChainIndexKey)
   379  		if pendingIdxByteSlice != nil {
   380  			wire.ReadBinaryBytes(pendingIdxByteSlice, &idx)
   381  		}
   382  		// Check if chain id has been added already
   383  		for _, v := range idx {
   384  			if v.ChainID == cci.ChainId {
   385  				return
   386  			}
   387  		}
   388  		// Pass the check, add the key to idx
   389  		idx = append(idx, pendingIdxData{cci.ChainId, cci.StartBlock, cci.EndBlock})
   390  		db.SetSync(pendingChainIndexKey, wire.BinaryBytes(idx))
   391  	}
   392  }
   393  
   394  // DeletePendingChildChainData delete the pending child chain data from db with chain id
   395  func DeletePendingChildChainData(db dbm.DB, chainId string) {
   396  	pendingChainMtx.Lock()
   397  	defer pendingChainMtx.Unlock()
   398  
   399  	db.DeleteSync(calcPendingChainInfoKey(chainId))
   400  }
   401  
   402  // GetChildChainForLaunch get the child chain for pending db for launch
   403  func GetChildChainForLaunch(db dbm.DB, height *big.Int, stateDB *state.StateDB) (readyForLaunch []string, newPendingIdxBytes []byte, deleteChildChainIds []string) {
   404  	pendingChainMtx.Lock()
   405  	defer pendingChainMtx.Unlock()
   406  
   407  	// Get the Pending Index from db
   408  	var idx []pendingIdxData
   409  	pendingIdxByteSlice := db.Get(pendingChainIndexKey)
   410  	if pendingIdxByteSlice != nil {
   411  		wire.ReadBinaryBytes(pendingIdxByteSlice, &idx)
   412  	}
   413  
   414  	if len(idx) == 0 {
   415  		return
   416  	}
   417  
   418  	newPendingIdx := idx[:0]
   419  
   420  	for _, v := range idx {
   421  		if v.Start.Cmp(height) > 0 {
   422  			// skip it
   423  			newPendingIdx = append(newPendingIdx, v)
   424  		} else if v.End.Cmp(height) < 0 {
   425  			// Refund the Lock Balance
   426  			cci := GetPendingChildChainData(db, v.ChainID)
   427  			for _, jv := range cci.JoinedValidators {
   428  				stateDB.SubChildChainDepositBalance(jv.Address, v.ChainID, jv.DepositAmount)
   429  				stateDB.AddBalance(jv.Address, jv.DepositAmount)
   430  			}
   431  
   432  			officialMinimumDeposit := math.MustParseBig256(OFFICIAL_MINIMUM_DEPOSIT)
   433  			stateDB.AddBalance(cci.Owner, officialMinimumDeposit)
   434  			stateDB.SubChainBalance(cci.Owner, officialMinimumDeposit)
   435  			if stateDB.GetChainBalance(cci.Owner).Sign() != 0 {
   436  				log.Error("the chain balance is not 0 when create chain failed, watch out!!!")
   437  			}
   438  
   439  			// Add the Child Chain Id to Remove List, to be removed after the consensus
   440  			deleteChildChainIds = append(deleteChildChainIds, v.ChainID)
   441  			//db.DeleteSync(calcPendingChainInfoKey(v.ChainID))
   442  		} else {
   443  			// check condition
   444  			cci := GetPendingChildChainData(db, v.ChainID)
   445  			if len(cci.JoinedValidators) >= int(cci.MinValidators) && cci.TotalDeposit().Cmp(cci.MinDepositAmount) >= 0 {
   446  				// Deduct the Deposit
   447  				for _, jv := range cci.JoinedValidators {
   448  					// Deposit will move to the Child Chain Account
   449  					stateDB.SubChildChainDepositBalance(jv.Address, v.ChainID, jv.DepositAmount)
   450  					stateDB.AddChainBalance(cci.Owner, jv.DepositAmount)
   451  				}
   452  				// Append the Chain ID to Ready Launch List
   453  				readyForLaunch = append(readyForLaunch, v.ChainID)
   454  			} else {
   455  				newPendingIdx = append(newPendingIdx, v)
   456  			}
   457  		}
   458  	}
   459  
   460  	if len(newPendingIdx) != len(idx) {
   461  		// Set the Bytes to Update the Pending Idx
   462  		newPendingIdxBytes = wire.BinaryBytes(newPendingIdx)
   463  		//db.SetSync(pendingChainIndexKey, wire.BinaryBytes(newPendingIdx))
   464  	}
   465  
   466  	// Return the ready for launch Child Chain
   467  	return
   468  }
   469  
   470  func ProcessPostPendingData(db dbm.DB, newPendingIdxBytes []byte, deleteChildChainIds []string) {
   471  	pendingChainMtx.Lock()
   472  	defer pendingChainMtx.Unlock()
   473  
   474  	// Remove the Child Chain
   475  	for _, id := range deleteChildChainIds {
   476  		db.DeleteSync(calcPendingChainInfoKey(id))
   477  	}
   478  
   479  	// Update the Idx Bytes
   480  	if newPendingIdxBytes != nil {
   481  		db.SetSync(pendingChainIndexKey, newPendingIdxBytes)
   482  	}
   483  }