github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/neatptc/backend.go (about) 1 package neatptc 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "runtime" 8 "sync" 9 "sync/atomic" 10 11 "github.com/neatlab/neatio/chain/accounts" 12 "github.com/neatlab/neatio/chain/consensus" 13 "github.com/neatlab/neatio/chain/consensus/neatcon" 14 ntcBackend "github.com/neatlab/neatio/chain/consensus/neatcon" 15 "github.com/neatlab/neatio/chain/core" 16 "github.com/neatlab/neatio/chain/core/bloombits" 17 "github.com/neatlab/neatio/chain/core/datareduction" 18 "github.com/neatlab/neatio/chain/core/rawdb" 19 "github.com/neatlab/neatio/chain/core/types" 20 "github.com/neatlab/neatio/chain/core/vm" 21 "github.com/neatlab/neatio/chain/log" 22 "github.com/neatlab/neatio/internal/neatapi" 23 "github.com/neatlab/neatio/neatdb" 24 "github.com/neatlab/neatio/neatptc/downloader" 25 "github.com/neatlab/neatio/neatptc/filters" 26 "github.com/neatlab/neatio/neatptc/gasprice" 27 "github.com/neatlab/neatio/network/node" 28 "github.com/neatlab/neatio/network/p2p" 29 "github.com/neatlab/neatio/network/rpc" 30 "github.com/neatlab/neatio/params" 31 "github.com/neatlab/neatio/utilities/common" 32 "github.com/neatlab/neatio/utilities/common/hexutil" 33 "github.com/neatlab/neatio/utilities/event" 34 "github.com/neatlab/neatio/utilities/miner" 35 "github.com/neatlab/neatio/utilities/rlp" 36 "gopkg.in/urfave/cli.v1" 37 ) 38 39 type LesServer interface { 40 Start(srvr *p2p.Server) 41 Stop() 42 Protocols() []p2p.Protocol 43 SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) 44 } 45 46 type NeatIO struct { 47 config *Config 48 chainConfig *params.ChainConfig 49 50 shutdownChan chan bool 51 52 txPool *core.TxPool 53 blockchain *core.BlockChain 54 protocolManager *ProtocolManager 55 56 chainDb neatdb.Database 57 pruneDb neatdb.Database 58 59 eventMux *event.TypeMux 60 engine consensus.NeatCon 61 accountManager *accounts.Manager 62 63 bloomRequests chan chan *bloombits.Retrieval 64 bloomIndexer *core.ChainIndexer 65 66 ApiBackend *EthApiBackend 67 68 miner *miner.Miner 69 gasPrice *big.Int 70 coinbase common.Address 71 solcPath string 72 73 networkId uint64 74 netRPCService *neatapi.PublicNetAPI 75 76 lock sync.RWMutex 77 } 78 79 func New(ctx *node.ServiceContext, config *Config, cliCtx *cli.Context, 80 cch core.CrossChainHelper, logger log.Logger, isTestnet bool) (*NeatIO, error) { 81 82 if !config.SyncMode.IsValid() { 83 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 84 } 85 chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles, "neatio/db/chaindata/") 86 if err != nil { 87 return nil, err 88 } 89 pruneDb, err := ctx.OpenDatabase("prunedata", config.DatabaseCache, config.DatabaseHandles, "neatio/db/prune/") 90 if err != nil { 91 return nil, err 92 } 93 94 isMainChain := params.IsMainChain(ctx.ChainId()) 95 96 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithDefault(chainDb, config.Genesis, isMainChain, isTestnet) 97 if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { 98 return nil, genesisErr 99 } 100 101 chainConfig.ConstantinopleBlock = big.NewInt(0) 102 chainConfig.PetersburgBlock = big.NewInt(0) 103 chainConfig.IstanbulBlock = big.NewInt(0) 104 105 chainConfig.ChainLogger = logger 106 logger.Info("Initialised chain configuration", "config", chainConfig) 107 108 neatChain := &NeatIO{ 109 config: config, 110 chainDb: chainDb, 111 pruneDb: pruneDb, 112 chainConfig: chainConfig, 113 eventMux: ctx.EventMux, 114 accountManager: ctx.AccountManager, 115 engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb, cliCtx, cch), 116 shutdownChan: make(chan bool), 117 networkId: config.NetworkId, 118 gasPrice: config.MinerGasPrice, 119 coinbase: config.Coinbase, 120 solcPath: config.SolcPath, 121 bloomRequests: make(chan chan *bloombits.Retrieval), 122 bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks), 123 } 124 125 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 126 var dbVer = "<nil>" 127 if bcVersion != nil { 128 dbVer = fmt.Sprintf("%d", *bcVersion) 129 } 130 logger.Info("Initialising NeatIO protocol", "versions", neatChain.engine.Protocol().Versions, "network", config.NetworkId, "dbversion", dbVer) 131 132 if !config.SkipBcVersionCheck { 133 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 134 return nil, fmt.Errorf("database version is v%d, Neatio %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) 135 } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { 136 logger.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) 137 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 138 } 139 } 140 var ( 141 vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} 142 cacheConfig = &core.CacheConfig{ 143 TrieCleanLimit: config.TrieCleanCache, 144 145 TrieDirtyLimit: config.TrieDirtyCache, 146 TrieDirtyDisabled: config.NoPruning, 147 TrieTimeLimit: config.TrieTimeout, 148 } 149 ) 150 151 neatChain.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, neatChain.chainConfig, neatChain.engine, vmConfig, cch) 152 if err != nil { 153 return nil, err 154 } 155 156 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 157 logger.Warn("Rewinding chain to upgrade configuration", "err", compat) 158 neatChain.blockchain.SetHead(compat.RewindTo) 159 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 160 } 161 neatChain.bloomIndexer.Start(neatChain.blockchain) 162 163 if config.TxPool.Journal != "" { 164 config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) 165 } 166 neatChain.txPool = core.NewTxPool(config.TxPool, neatChain.chainConfig, neatChain.blockchain, cch) 167 168 if neatChain.protocolManager, err = NewProtocolManager(neatChain.chainConfig, config.SyncMode, config.NetworkId, neatChain.eventMux, neatChain.txPool, neatChain.engine, neatChain.blockchain, chainDb, cch); err != nil { 169 return nil, err 170 } 171 neatChain.miner = miner.New(neatChain, neatChain.chainConfig, neatChain.EventMux(), neatChain.engine, config.MinerGasFloor, config.MinerGasCeil, cch) 172 neatChain.miner.SetExtra(makeExtraData(config.ExtraData)) 173 174 neatChain.ApiBackend = &EthApiBackend{neatChain, nil, cch} 175 gpoParams := config.GPO 176 if gpoParams.Default == nil { 177 gpoParams.Default = config.MinerGasPrice 178 } 179 neatChain.ApiBackend.gpo = gasprice.NewOracle(neatChain.ApiBackend, gpoParams) 180 181 return neatChain, nil 182 } 183 184 func makeExtraData(extra []byte) []byte { 185 if len(extra) == 0 { 186 187 extra, _ = rlp.EncodeToBytes([]interface{}{ 188 uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), 189 "neatio", 190 runtime.Version(), 191 runtime.GOOS, 192 }) 193 } 194 if uint64(len(extra)) > params.MaximumExtraDataSize { 195 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 196 extra = nil 197 } 198 return extra 199 } 200 201 func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db neatdb.Database, 202 cliCtx *cli.Context, cch core.CrossChainHelper) consensus.NeatCon { 203 204 if chainConfig.NeatCon.Epoch != 0 { 205 config.NeatCon.Epoch = chainConfig.NeatCon.Epoch 206 } 207 config.NeatCon.ProposerPolicy = neatcon.ProposerPolicy(chainConfig.NeatCon.ProposerPolicy) 208 return ntcBackend.New(chainConfig, cliCtx, ctx.NodeKey(), cch) 209 } 210 211 func (s *NeatIO) APIs() []rpc.API { 212 213 apis := neatapi.GetAPIs(s.ApiBackend, s.solcPath) 214 215 apis = append(apis, s.engine.APIs(s.BlockChain())...) 216 217 apis = append(apis, []rpc.API{ 218 { 219 Namespace: "eth", 220 Version: "1.0", 221 Service: NewPublicEthereumAPI(s), 222 Public: true, 223 }, { 224 Namespace: "eth", 225 Version: "1.0", 226 Service: NewPublicMinerAPI(s), 227 Public: true, 228 }, { 229 Namespace: "eth", 230 Version: "1.0", 231 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 232 Public: true, 233 }, { 234 Namespace: "neat", 235 Version: "1.0", 236 Service: NewPublicEthereumAPI(s), 237 Public: true, 238 }, { 239 Namespace: "neat", 240 Version: "1.0", 241 Service: NewPublicMinerAPI(s), 242 Public: true, 243 }, { 244 Namespace: "neat", 245 Version: "1.0", 246 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 247 Public: true, 248 }, { 249 Namespace: "miner", 250 Version: "1.0", 251 Service: NewPrivateMinerAPI(s), 252 Public: false, 253 }, { 254 Namespace: "eth", 255 Version: "1.0", 256 Service: filters.NewPublicFilterAPI(s.ApiBackend, false), 257 Public: true, 258 }, { 259 Namespace: "neat", 260 Version: "1.0", 261 Service: filters.NewPublicFilterAPI(s.ApiBackend, false), 262 Public: true, 263 }, { 264 Namespace: "admin", 265 Version: "1.0", 266 Service: NewPrivateAdminAPI(s), 267 }, { 268 Namespace: "debug", 269 Version: "1.0", 270 Service: NewPublicDebugAPI(s), 271 Public: true, 272 }, { 273 Namespace: "debug", 274 Version: "1.0", 275 Service: NewPrivateDebugAPI(s.chainConfig, s), 276 }, { 277 Namespace: "net", 278 Version: "1.0", 279 Service: s.netRPCService, 280 Public: true, 281 }, 282 }...) 283 return apis 284 } 285 286 func (s *NeatIO) ResetWithGenesisBlock(gb *types.Block) { 287 s.blockchain.ResetWithGenesisBlock(gb) 288 } 289 290 func (s *NeatIO) Coinbase() (eb common.Address, err error) { 291 if neatcon, ok := s.engine.(consensus.NeatCon); ok { 292 eb = neatcon.PrivateValidator() 293 if eb != (common.Address{}) { 294 return eb, nil 295 } else { 296 return eb, errors.New("private validator missing") 297 } 298 } else { 299 s.lock.RLock() 300 coinbase := s.coinbase 301 s.lock.RUnlock() 302 303 if coinbase != (common.Address{}) { 304 return coinbase, nil 305 } 306 if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { 307 if accounts := wallets[0].Accounts(); len(accounts) > 0 { 308 coinbase := accounts[0].Address 309 310 s.lock.Lock() 311 s.coinbase = coinbase 312 s.lock.Unlock() 313 314 log.Info("Coinbase automatically configured", "address", coinbase) 315 return coinbase, nil 316 } 317 } 318 } 319 return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") 320 } 321 322 func (self *NeatIO) SetCoinbase(coinbase common.Address) { 323 324 if _, ok := self.engine.(consensus.NeatCon); ok { 325 log.Error("Cannot set etherbase in NeatCon consensus") 326 return 327 } 328 329 self.lock.Lock() 330 self.coinbase = coinbase 331 self.lock.Unlock() 332 333 self.miner.SetCoinbase(coinbase) 334 } 335 336 func (s *NeatIO) StartMining(local bool) error { 337 var eb common.Address 338 if neatcon, ok := s.engine.(consensus.NeatCon); ok { 339 eb = neatcon.PrivateValidator() 340 if (eb == common.Address{}) { 341 log.Error("Cannot start mining without private validator") 342 return errors.New("private validator file missing") 343 } 344 } else { 345 _, err := s.Coinbase() 346 if err != nil { 347 log.Error("Cannot start mining without etherbase", "err", err) 348 return fmt.Errorf("etherbase missing: %v", err) 349 } 350 } 351 352 if local { 353 354 atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) 355 } 356 go s.miner.Start(eb) 357 return nil 358 } 359 360 func (s *NeatIO) StopMining() { s.miner.Stop() } 361 func (s *NeatIO) IsMining() bool { return s.miner.Mining() } 362 func (s *NeatIO) Miner() *miner.Miner { return s.miner } 363 364 func (s *NeatIO) ChainConfig() *params.ChainConfig { return s.chainConfig } 365 func (s *NeatIO) AccountManager() *accounts.Manager { return s.accountManager } 366 func (s *NeatIO) BlockChain() *core.BlockChain { return s.blockchain } 367 func (s *NeatIO) TxPool() *core.TxPool { return s.txPool } 368 func (s *NeatIO) EventMux() *event.TypeMux { return s.eventMux } 369 func (s *NeatIO) Engine() consensus.NeatCon { return s.engine } 370 func (s *NeatIO) ChainDb() neatdb.Database { return s.chainDb } 371 func (s *NeatIO) IsListening() bool { return true } 372 func (s *NeatIO) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } 373 func (s *NeatIO) NetVersion() uint64 { return s.networkId } 374 func (s *NeatIO) Downloader() *downloader.Downloader { return s.protocolManager.downloader } 375 376 func (s *NeatIO) Protocols() []p2p.Protocol { 377 return s.protocolManager.SubProtocols 378 } 379 380 func (s *NeatIO) Start(srvr *p2p.Server) error { 381 382 s.startBloomHandlers() 383 384 s.netRPCService = neatapi.NewPublicNetAPI(srvr, s.NetVersion()) 385 386 maxPeers := srvr.MaxPeers 387 388 s.protocolManager.Start(maxPeers) 389 390 go s.loopForMiningEvent() 391 392 if s.config.PruneStateData && s.chainConfig.NeatChainId == "side_0" { 393 go s.StartScanAndPrune(0) 394 } 395 396 return nil 397 } 398 399 func (s *NeatIO) Stop() error { 400 s.bloomIndexer.Close() 401 s.blockchain.Stop() 402 s.protocolManager.Stop() 403 s.txPool.Stop() 404 s.miner.Stop() 405 s.engine.Close() 406 s.miner.Close() 407 s.eventMux.Stop() 408 409 s.chainDb.Close() 410 s.pruneDb.Close() 411 close(s.shutdownChan) 412 413 return nil 414 } 415 416 func (s *NeatIO) loopForMiningEvent() { 417 418 startMiningCh := make(chan core.StartMiningEvent, 1) 419 startMiningSub := s.blockchain.SubscribeStartMiningEvent(startMiningCh) 420 421 stopMiningCh := make(chan core.StopMiningEvent, 1) 422 stopMiningSub := s.blockchain.SubscribeStopMiningEvent(stopMiningCh) 423 424 defer startMiningSub.Unsubscribe() 425 defer stopMiningSub.Unsubscribe() 426 427 for { 428 select { 429 case <-startMiningCh: 430 if !s.IsMining() { 431 s.lock.RLock() 432 price := s.gasPrice 433 s.lock.RUnlock() 434 s.txPool.SetGasPrice(price) 435 s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine will start shortly") 436 s.engine.(consensus.NeatCon).ForceStart() 437 s.StartMining(true) 438 } else { 439 s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine already started") 440 } 441 case <-stopMiningCh: 442 if s.IsMining() { 443 s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine will stop shortly") 444 s.StopMining() 445 } else { 446 s.chainConfig.ChainLogger.Info("NeatCon Consensus Engine already stopped") 447 } 448 case <-startMiningSub.Err(): 449 return 450 case <-stopMiningSub.Err(): 451 return 452 } 453 } 454 } 455 456 func (s *NeatIO) StartScanAndPrune(blockNumber uint64) { 457 458 if datareduction.StartPruning() { 459 log.Info("Data Reduction - Start") 460 } else { 461 log.Info("Data Reduction - Pruning is already running") 462 return 463 } 464 465 latestBlockNumber := s.blockchain.CurrentHeader().Number.Uint64() 466 if blockNumber == 0 || blockNumber >= latestBlockNumber { 467 blockNumber = latestBlockNumber 468 log.Infof("Data Reduction - Last block number %v", blockNumber) 469 } else { 470 log.Infof("Data Reduction - User defined Last block number %v", blockNumber) 471 } 472 473 ps := rawdb.ReadHeadScanNumber(s.pruneDb) 474 var scanNumber uint64 475 if ps != nil { 476 scanNumber = *ps 477 } 478 479 pp := rawdb.ReadHeadPruneNumber(s.pruneDb) 480 var pruneNumber uint64 481 if pp != nil { 482 pruneNumber = *pp 483 } 484 log.Infof("Data Reduction - Last scan number %v, prune number %v", scanNumber, pruneNumber) 485 486 pruneProcessor := datareduction.NewPruneProcessor(s.chainDb, s.pruneDb, s.blockchain, s.config.PruneBlockData) 487 488 lastScanNumber, lastPruneNumber := pruneProcessor.Process(blockNumber, scanNumber, pruneNumber) 489 log.Infof("Data Reduction - After prune, last number scan %v, prune number %v", lastScanNumber, lastPruneNumber) 490 if s.config.PruneBlockData { 491 for i := uint64(1); i < lastPruneNumber; i++ { 492 rawdb.DeleteBody(s.chainDb, rawdb.ReadCanonicalHash(s.chainDb, i), i) 493 } 494 log.Infof("deleted block from 1 to %v", lastPruneNumber) 495 } 496 log.Info("Data Reduction - Completed") 497 498 datareduction.StopPruning() 499 }