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 }