github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/intprotocol/backend.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package intprotocol implements the Ethereum protocol. 18 package intprotocol 19 20 import ( 21 "errors" 22 "fmt" 23 "github.com/intfoundation/intchain/accounts" 24 "github.com/intfoundation/intchain/common" 25 "github.com/intfoundation/intchain/common/hexutil" 26 "github.com/intfoundation/intchain/consensus" 27 "github.com/intfoundation/intchain/consensus/ipbft" 28 tendermintBackend "github.com/intfoundation/intchain/consensus/ipbft" 29 "github.com/intfoundation/intchain/core" 30 "github.com/intfoundation/intchain/core/bloombits" 31 "github.com/intfoundation/intchain/core/datareduction" 32 "github.com/intfoundation/intchain/core/rawdb" 33 "github.com/intfoundation/intchain/core/types" 34 "github.com/intfoundation/intchain/core/vm" 35 "github.com/intfoundation/intchain/event" 36 "github.com/intfoundation/intchain/intdb" 37 "github.com/intfoundation/intchain/internal/intapi" 38 "github.com/intfoundation/intchain/intprotocol/downloader" 39 "github.com/intfoundation/intchain/intprotocol/filters" 40 "github.com/intfoundation/intchain/intprotocol/gasprice" 41 "github.com/intfoundation/intchain/log" 42 "github.com/intfoundation/intchain/miner" 43 "github.com/intfoundation/intchain/node" 44 "github.com/intfoundation/intchain/p2p" 45 "github.com/intfoundation/intchain/params" 46 "github.com/intfoundation/intchain/rlp" 47 "github.com/intfoundation/intchain/rpc" 48 "gopkg.in/urfave/cli.v1" 49 "math/big" 50 "runtime" 51 "sync" 52 "sync/atomic" 53 ) 54 55 type LesServer interface { 56 Start(srvr *p2p.Server) 57 Stop() 58 Protocols() []p2p.Protocol 59 SetBloomBitsIndexer(bbIndexer *core.ChainIndexer) 60 } 61 62 // IntChain implements the INT Chain full node service. 63 type IntChain struct { 64 config *Config 65 chainConfig *params.ChainConfig 66 67 // Channel for shutting down the service 68 shutdownChan chan bool // Channel for shutting down the intChain 69 70 // Handlers 71 txPool *core.TxPool 72 blockchain *core.BlockChain 73 protocolManager *ProtocolManager 74 75 // DB interfaces 76 chainDb intdb.Database // Block chain database 77 pruneDb intdb.Database // Prune data database 78 79 eventMux *event.TypeMux 80 engine consensus.IPBFT 81 accountManager *accounts.Manager 82 83 bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests 84 bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports 85 86 ApiBackend *EthApiBackend 87 88 miner *miner.Miner 89 gasPrice *big.Int 90 coinbase common.Address 91 solcPath string 92 93 networkId uint64 94 netRPCService *intapi.PublicNetAPI 95 96 lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) 97 } 98 99 // New creates a new INT Chain object (including the 100 // initialisation of the common INT Chain object) 101 func New(ctx *node.ServiceContext, config *Config, cliCtx *cli.Context, 102 cch core.CrossChainHelper, logger log.Logger, isTestnet bool) (*IntChain, error) { 103 104 if !config.SyncMode.IsValid() { 105 return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) 106 } 107 chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles, "intchain/db/chaindata/") 108 if err != nil { 109 return nil, err 110 } 111 pruneDb, err := ctx.OpenDatabase("prunedata", config.DatabaseCache, config.DatabaseHandles, "intchain/db/prune/") 112 if err != nil { 113 return nil, err 114 } 115 116 isMainChain := params.IsMainChain(ctx.ChainId()) 117 118 chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithDefault(chainDb, config.Genesis, isMainChain, isTestnet) 119 if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { 120 return nil, genesisErr 121 } 122 123 //directly support corresponding instruction set 124 chainConfig.ConstantinopleBlock = big.NewInt(0) 125 chainConfig.PetersburgBlock = big.NewInt(0) 126 chainConfig.IstanbulBlock = big.NewInt(0) 127 128 chainConfig.ChainLogger = logger 129 logger.Info("Initialised chain configuration", "config", chainConfig) 130 131 intChain := &IntChain{ 132 config: config, 133 chainDb: chainDb, 134 pruneDb: pruneDb, 135 chainConfig: chainConfig, 136 eventMux: ctx.EventMux, 137 accountManager: ctx.AccountManager, 138 engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb, cliCtx, cch), 139 shutdownChan: make(chan bool), 140 networkId: config.NetworkId, 141 gasPrice: config.MinerGasPrice, 142 coinbase: config.Coinbase, 143 solcPath: config.SolcPath, 144 bloomRequests: make(chan chan *bloombits.Retrieval), 145 bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks), 146 } 147 148 bcVersion := rawdb.ReadDatabaseVersion(chainDb) 149 var dbVer = "<nil>" 150 if bcVersion != nil { 151 dbVer = fmt.Sprintf("%d", *bcVersion) 152 } 153 logger.Info("Initialising IntChain protocol", "versions", intChain.engine.Protocol().Versions, "network", config.NetworkId, "dbversion", dbVer) 154 //logger.Info("Initialising intchain protocol", "network", config.NetworkId, "dbversion", dbVer) 155 156 if !config.SkipBcVersionCheck { 157 if bcVersion != nil && *bcVersion > core.BlockChainVersion { 158 return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion) 159 } else if bcVersion == nil || *bcVersion < core.BlockChainVersion { 160 logger.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion) 161 rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) 162 } 163 } 164 var ( 165 vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} 166 cacheConfig = &core.CacheConfig{ 167 TrieCleanLimit: config.TrieCleanCache, 168 169 TrieDirtyLimit: config.TrieDirtyCache, 170 TrieDirtyDisabled: config.NoPruning, 171 TrieTimeLimit: config.TrieTimeout, 172 } 173 ) 174 //eth.engine = CreateConsensusEngine(ctx, config, chainConfig, chainDb, cliCtx, cch) 175 176 intChain.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, intChain.chainConfig, intChain.engine, vmConfig, cch) 177 if err != nil { 178 return nil, err 179 } 180 181 // Rewind the chain in case of an incompatible config upgrade. 182 if compat, ok := genesisErr.(*params.ConfigCompatError); ok { 183 logger.Warn("Rewinding chain to upgrade configuration", "err", compat) 184 intChain.blockchain.SetHead(compat.RewindTo) 185 rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) 186 } 187 intChain.bloomIndexer.Start(intChain.blockchain) 188 189 if config.TxPool.Journal != "" { 190 config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) 191 } 192 intChain.txPool = core.NewTxPool(config.TxPool, intChain.chainConfig, intChain.blockchain, cch) 193 194 if intChain.protocolManager, err = NewProtocolManager(intChain.chainConfig, config.SyncMode, config.NetworkId, intChain.eventMux, intChain.txPool, intChain.engine, intChain.blockchain, chainDb, cch); err != nil { 195 return nil, err 196 } 197 intChain.miner = miner.New(intChain, intChain.chainConfig, intChain.EventMux(), intChain.engine, config.MinerGasFloor, config.MinerGasCeil, cch) 198 intChain.miner.SetExtra(makeExtraData(config.ExtraData)) 199 200 intChain.ApiBackend = &EthApiBackend{intChain, nil, cch} 201 gpoParams := config.GPO 202 if gpoParams.Default == nil { 203 gpoParams.Default = config.MinerGasPrice 204 } 205 intChain.ApiBackend.gpo = gasprice.NewOracle(intChain.ApiBackend, gpoParams) 206 207 return intChain, nil 208 } 209 210 func makeExtraData(extra []byte) []byte { 211 if len(extra) == 0 { 212 // create default extradata 213 extra, _ = rlp.EncodeToBytes([]interface{}{ 214 uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), 215 "intchain", 216 runtime.Version(), 217 runtime.GOOS, 218 }) 219 } 220 if uint64(len(extra)) > params.MaximumExtraDataSize { 221 log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize) 222 extra = nil 223 } 224 return extra 225 } 226 227 // CreateConsensusEngine creates the required type of consensus engine instance for an IntChain service 228 func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db intdb.Database, 229 cliCtx *cli.Context, cch core.CrossChainHelper) consensus.IPBFT { 230 // If IPBFT is requested, set it up 231 if chainConfig.IPBFT.Epoch != 0 { 232 config.IPBFT.Epoch = chainConfig.IPBFT.Epoch 233 } 234 config.IPBFT.ProposerPolicy = ipbft.ProposerPolicy(chainConfig.IPBFT.ProposerPolicy) 235 return tendermintBackend.New(chainConfig, cliCtx, ctx.NodeKey(), cch) 236 } 237 238 // APIs returns the collection of RPC services the IntChain package offers. 239 // NOTE, some of these services probably need to be moved to somewhere else. 240 func (s *IntChain) APIs() []rpc.API { 241 242 apis := intapi.GetAPIs(s.ApiBackend, s.solcPath) 243 // Append any APIs exposed explicitly by the consensus engine 244 apis = append(apis, s.engine.APIs(s.BlockChain())...) 245 // Append all the local APIs and return 246 apis = append(apis, []rpc.API{ 247 { 248 Namespace: "eth", 249 Version: "1.0", 250 Service: NewPublicEthereumAPI(s), 251 Public: true, 252 }, { 253 Namespace: "eth", 254 Version: "1.0", 255 Service: NewPublicMinerAPI(s), 256 Public: true, 257 }, { 258 Namespace: "eth", 259 Version: "1.0", 260 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 261 Public: true, 262 }, { 263 Namespace: "int", 264 Version: "1.0", 265 Service: NewPublicEthereumAPI(s), 266 Public: true, 267 }, { 268 Namespace: "int", 269 Version: "1.0", 270 Service: NewPublicMinerAPI(s), 271 Public: true, 272 }, { 273 Namespace: "int", 274 Version: "1.0", 275 Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), 276 Public: true, 277 }, { 278 Namespace: "miner", 279 Version: "1.0", 280 Service: NewPrivateMinerAPI(s), 281 Public: false, 282 }, { 283 Namespace: "eth", 284 Version: "1.0", 285 Service: filters.NewPublicFilterAPI(s.ApiBackend, false), 286 Public: true, 287 }, { 288 Namespace: "int", 289 Version: "1.0", 290 Service: filters.NewPublicFilterAPI(s.ApiBackend, false), 291 Public: true, 292 }, { 293 Namespace: "admin", 294 Version: "1.0", 295 Service: NewPrivateAdminAPI(s), 296 }, { 297 Namespace: "debug", 298 Version: "1.0", 299 Service: NewPublicDebugAPI(s), 300 Public: true, 301 }, { 302 Namespace: "debug", 303 Version: "1.0", 304 Service: NewPrivateDebugAPI(s.chainConfig, s), 305 }, { 306 Namespace: "net", 307 Version: "1.0", 308 Service: s.netRPCService, 309 Public: true, 310 }, 311 }...) 312 return apis 313 } 314 315 func (s *IntChain) ResetWithGenesisBlock(gb *types.Block) { 316 s.blockchain.ResetWithGenesisBlock(gb) 317 } 318 319 func (s *IntChain) Coinbase() (eb common.Address, err error) { 320 if ipbft, ok := s.engine.(consensus.IPBFT); ok { 321 eb = ipbft.PrivateValidator() 322 if eb != (common.Address{}) { 323 return eb, nil 324 } else { 325 return eb, errors.New("private validator missing") 326 } 327 } else { 328 s.lock.RLock() 329 coinbase := s.coinbase 330 s.lock.RUnlock() 331 332 if coinbase != (common.Address{}) { 333 return coinbase, nil 334 } 335 if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { 336 if accounts := wallets[0].Accounts(); len(accounts) > 0 { 337 coinbase := accounts[0].Address 338 339 s.lock.Lock() 340 s.coinbase = coinbase 341 s.lock.Unlock() 342 343 log.Info("Coinbase automatically configured", "address", coinbase) 344 return coinbase, nil 345 } 346 } 347 } 348 return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") 349 } 350 351 // set in js console via admin interface or wrapper from cli flags 352 func (self *IntChain) SetCoinbase(coinbase common.Address) { 353 354 if _, ok := self.engine.(consensus.IPBFT); ok { 355 log.Error("Cannot set etherbase in IPBFT consensus") 356 return 357 } 358 359 self.lock.Lock() 360 self.coinbase = coinbase 361 self.lock.Unlock() 362 363 self.miner.SetCoinbase(coinbase) 364 } 365 366 func (s *IntChain) StartMining(local bool) error { 367 var eb common.Address 368 if ipbft, ok := s.engine.(consensus.IPBFT); ok { 369 eb = ipbft.PrivateValidator() 370 if (eb == common.Address{}) { 371 log.Error("Cannot start mining without private validator") 372 return errors.New("private validator missing") 373 } 374 } else { 375 _, err := s.Coinbase() 376 if err != nil { 377 log.Error("Cannot start mining without etherbase", "err", err) 378 return fmt.Errorf("etherbase missing: %v", err) 379 } 380 } 381 382 if local { 383 // If local (CPU) mining is started, we can disable the transaction rejection 384 // mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous 385 // so noone will ever hit this path, whereas marking sync done on CPU mining 386 // will ensure that private networks work in single miner mode too. 387 atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) 388 } 389 go s.miner.Start(eb) 390 return nil 391 } 392 393 func (s *IntChain) StopMining() { s.miner.Stop() } 394 func (s *IntChain) IsMining() bool { return s.miner.Mining() } 395 func (s *IntChain) Miner() *miner.Miner { return s.miner } 396 397 func (s *IntChain) ChainConfig() *params.ChainConfig { return s.chainConfig } 398 func (s *IntChain) AccountManager() *accounts.Manager { return s.accountManager } 399 func (s *IntChain) BlockChain() *core.BlockChain { return s.blockchain } 400 func (s *IntChain) TxPool() *core.TxPool { return s.txPool } 401 func (s *IntChain) EventMux() *event.TypeMux { return s.eventMux } 402 func (s *IntChain) Engine() consensus.IPBFT { return s.engine } 403 func (s *IntChain) ChainDb() intdb.Database { return s.chainDb } 404 func (s *IntChain) IsListening() bool { return true } // Always listening 405 func (s *IntChain) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } 406 func (s *IntChain) NetVersion() uint64 { return s.networkId } 407 func (s *IntChain) Downloader() *downloader.Downloader { return s.protocolManager.downloader } 408 409 // Protocols implements node.Service, returning all the currently configured 410 // network protocols to start. 411 func (s *IntChain) Protocols() []p2p.Protocol { 412 return s.protocolManager.SubProtocols 413 } 414 415 // Start implements node.Service, starting all internal goroutines needed by the 416 // INT Chain protocol implementation. 417 func (s *IntChain) Start(srvr *p2p.Server) error { 418 // Start the bloom bits servicing goroutines 419 s.startBloomHandlers() 420 421 // Start the RPC service 422 s.netRPCService = intapi.NewPublicNetAPI(srvr, s.NetVersion()) 423 424 // Figure out a max peers count based on the server limits 425 maxPeers := srvr.MaxPeers 426 427 // Start the networking layer and the light server if requested 428 s.protocolManager.Start(maxPeers) 429 430 // Start the Auto Mining Loop 431 go s.loopForMiningEvent() 432 433 // Start the Data Reduction 434 if s.config.PruneStateData && s.chainConfig.IntChainId == "child_0" { 435 go s.StartScanAndPrune(0) 436 } 437 438 return nil 439 } 440 441 // Stop implements node.Service, terminating all internal goroutines used by the 442 // IntChain protocol. 443 func (s *IntChain) Stop() error { 444 s.bloomIndexer.Close() 445 s.blockchain.Stop() 446 s.protocolManager.Stop() 447 s.txPool.Stop() 448 s.miner.Stop() 449 s.engine.Close() 450 s.miner.Close() 451 s.eventMux.Stop() 452 453 s.chainDb.Close() 454 s.pruneDb.Close() 455 close(s.shutdownChan) 456 457 return nil 458 } 459 460 func (s *IntChain) loopForMiningEvent() { 461 // Start/Stop mining Feed 462 startMiningCh := make(chan core.StartMiningEvent, 1) 463 startMiningSub := s.blockchain.SubscribeStartMiningEvent(startMiningCh) 464 465 stopMiningCh := make(chan core.StopMiningEvent, 1) 466 stopMiningSub := s.blockchain.SubscribeStopMiningEvent(stopMiningCh) 467 468 defer startMiningSub.Unsubscribe() 469 defer stopMiningSub.Unsubscribe() 470 471 for { 472 select { 473 case <-startMiningCh: 474 if !s.IsMining() { 475 s.lock.RLock() 476 price := s.gasPrice 477 s.lock.RUnlock() 478 s.txPool.SetGasPrice(price) 479 s.chainConfig.ChainLogger.Info("IPBFT Consensus Engine will be start shortly") 480 s.engine.(consensus.IPBFT).ForceStart() 481 s.StartMining(true) 482 } else { 483 s.chainConfig.ChainLogger.Info("IPBFT Consensus Engine already started") 484 } 485 case <-stopMiningCh: 486 if s.IsMining() { 487 s.chainConfig.ChainLogger.Info("IPBFT Consensus Engine will be stop shortly") 488 s.StopMining() 489 } else { 490 s.chainConfig.ChainLogger.Info("IPBFT Consensus Engine already stopped") 491 } 492 case <-startMiningSub.Err(): 493 return 494 case <-stopMiningSub.Err(): 495 return 496 } 497 } 498 } 499 500 func (s *IntChain) StartScanAndPrune(blockNumber uint64) { 501 502 if datareduction.StartPruning() { 503 log.Info("Data Reduction - Start") 504 } else { 505 log.Info("Data Reduction - Pruning is already running") 506 return 507 } 508 509 latestBlockNumber := s.blockchain.CurrentHeader().Number.Uint64() 510 if blockNumber == 0 || blockNumber >= latestBlockNumber { 511 blockNumber = latestBlockNumber 512 log.Infof("Data Reduction - Last block number %v", blockNumber) 513 } else { 514 log.Infof("Data Reduction - User defined Last block number %v", blockNumber) 515 } 516 517 ps := rawdb.ReadHeadScanNumber(s.pruneDb) 518 var scanNumber uint64 519 if ps != nil { 520 scanNumber = *ps 521 } 522 523 pp := rawdb.ReadHeadPruneNumber(s.pruneDb) 524 var pruneNumber uint64 525 if pp != nil { 526 pruneNumber = *pp 527 } 528 log.Infof("Data Reduction - Last scan number %v, prune number %v", scanNumber, pruneNumber) 529 530 pruneProcessor := datareduction.NewPruneProcessor(s.chainDb, s.pruneDb, s.blockchain, s.config.PruneBlockData) 531 532 lastScanNumber, lastPruneNumber := pruneProcessor.Process(blockNumber, scanNumber, pruneNumber) 533 log.Infof("Data Reduction - After prune, last number scan %v, prune number %v", lastScanNumber, lastPruneNumber) 534 if s.config.PruneBlockData { 535 for i := uint64(1); i < lastPruneNumber; i++ { 536 rawdb.DeleteBody(s.chainDb, rawdb.ReadCanonicalHash(s.chainDb, i), i) 537 } 538 log.Infof("deleted block from 1 to %v", lastPruneNumber) 539 } 540 log.Info("Data Reduction - Completed") 541 542 datareduction.StopPruning() 543 }