github.com/Gessiux/neatchain@v1.3.1/chain/core/chains_info.go (about)

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