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 }