// sync databases every minute. If flushing fails we exit immediatly. The system // may not continue under any circumstances. func (s *Ethereum) syncDatabases() { ticker := time.NewTicker(1 * time.Minute) done: for { select { case <-ticker.C: // don't change the order of database flushes if err := s.extraDb.Flush(); err != nil { glog.Fatalf("fatal error: flush extraDb: %v (Restart your node. We are aware of this issue)\n", err) } if err := s.stateDb.Flush(); err != nil { glog.Fatalf("fatal error: flush stateDb: %v (Restart your node. We are aware of this issue)\n", err) } if err := s.blockDb.Flush(); err != nil { glog.Fatalf("fatal error: flush blockDb: %v (Restart your node. We are aware of this issue)\n", err) } case <-s.shutdownChan: break done } } s.blockDb.Close() s.stateDb.Close() s.extraDb.Close() close(s.databasesClosed) }
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. func (bc *ChainManager) ResetWithGenesisBlock(genesis *types.Block) { bc.mu.Lock() defer bc.mu.Unlock() // Dump the entire block chain and purge the caches for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) { DeleteBlock(bc.chainDb, block.Hash()) } bc.headerCache.Purge() bc.bodyCache.Purge() bc.bodyRLPCache.Purge() bc.blockCache.Purge() bc.futureBlocks.Purge() // Prepare the genesis block and reinitialize the chain if err := WriteTd(bc.chainDb, genesis.Hash(), genesis.Difficulty()); err != nil { glog.Fatalf("failed to write genesis block TD: %v", err) } if err := WriteBlock(bc.chainDb, genesis); err != nil { glog.Fatalf("failed to write genesis block: %v", err) } bc.genesisBlock = genesis bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock bc.setTotalDifficulty(genesis.Difficulty()) }
// insert injects a new head block into the current block chain. This method // assumes that the block is indeed a true head. It will also reset the head // header and the head fast sync block to this very same block if they are older // or if they are on a different side chain. // // Note, this function assumes that the `mu` mutex is held! func (bc *BlockChain) insert(block *types.Block) { // If the block is on a side chain or an unknown one, force other heads onto it too updateHeads := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash() // Add the block to the canonical chain number scheme and mark as the head if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { glog.Fatalf("failed to insert block number: %v", err) } if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to insert head block hash: %v", err) } bc.currentBlock = block // If the block is better than out head or is on a different chain, force update heads if updateHeads { if err := WriteHeadHeaderHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } bc.currentHeader = block.Header() if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to insert head fast block hash: %v", err) } bc.currentFastBlock = block } }
// NodeKey retrieves the currently configured private key of the node, checking // first any manually set key, falling back to the one found in the configured // data folder. If no key can be found, a new one is generated. func (c *Config) NodeKey() *ecdsa.PrivateKey { // Use any specifically configured key if c.PrivateKey != nil { return c.PrivateKey } // Generate ephemeral key if no datadir is being used if c.DataDir == "" { key, err := crypto.GenerateKey() if err != nil { glog.Fatalf("Failed to generate ephemeral node key: %v", err) } return key } // Fall back to persistent key from the data directory keyfile := filepath.Join(c.DataDir, datadirPrivateKey) if key, err := crypto.LoadECDSA(keyfile); err == nil { return key } // No persistent key found, generate and store a new one key, err := crypto.GenerateKey() if err != nil { glog.Fatalf("Failed to generate node key: %v", err) } if err := crypto.SaveECDSA(keyfile, key); err != nil { glog.V(logger.Error).Infof("Failed to persist node key: %v", err) } return key }
// WriteHeader writes a header into the local chain, given that its parent is // already known. If the total difficulty of the newly inserted header becomes // greater than the current known TD, the canonical chain is re-routed. // // Note: This method is not concurrent-safe with inserting blocks simultaneously // into the chain, as side effects caused by reorganisations cannot be emulated // without the real blocks. Hence, writing headers directly should only be done // in two scenarios: pure-header mode of operation (light clients), or properly // separated header/block phases (non-archive clients). func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) { // Cache some values to prevent constant recalculation var ( hash = header.Hash() number = header.Number.Uint64() ) // Calculate the total difficulty of the header ptd := hc.GetTd(header.ParentHash) if ptd == nil { return NonStatTy, ParentError(header.ParentHash) } localTd := hc.GetTd(hc.currentHeaderHash) externTd := new(big.Int).Add(header.Difficulty, ptd) // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { // Delete any canonical number assignments above the new head for i := number + 1; GetCanonicalHash(hc.chainDb, i) != (common.Hash{}); i++ { DeleteCanonicalHash(hc.chainDb, i) } // Overwrite any stale canonical number assignments var ( headHash = header.ParentHash headHeader = hc.GetHeader(headHash) headNumber = headHeader.Number.Uint64() ) for GetCanonicalHash(hc.chainDb, headNumber) != headHash { WriteCanonicalHash(hc.chainDb, headHash, headNumber) headHash = headHeader.ParentHash headHeader = hc.GetHeader(headHash) headNumber = headHeader.Number.Uint64() } // Extend the canonical chain with the new header if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil { glog.Fatalf("failed to insert header number: %v", err) } if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header) status = CanonStatTy } else { status = SideStatTy } // Irrelevant of the canonical status, write the header itself to the database if err := hc.WriteTd(hash, externTd); err != nil { glog.Fatalf("failed to write header total difficulty: %v", err) } if err := WriteHeader(hc.chainDb, header); err != nil { glog.Fatalf("failed to write header contents: %v", err) } hc.headerCache.Add(hash, header) return }
// writeHeader writes a header into the local chain, given that its parent is // already known. If the total difficulty of the newly inserted header becomes // greater than the current known TD, the canonical chain is re-routed. // // Note: This method is not concurrent-safe with inserting blocks simultaneously // into the chain, as side effects caused by reorganisations cannot be emulated // without the real blocks. Hence, writing headers directly should only be done // in two scenarios: pure-header mode of operation (light clients), or properly // separated header/block phases (non-archive clients). func (self *BlockChain) writeHeader(header *types.Header) error { self.wg.Add(1) defer self.wg.Done() // Calculate the total difficulty of the header ptd := self.GetTd(header.ParentHash) if ptd == nil { return ParentError(header.ParentHash) } localTd := self.GetTd(self.currentHeader.Hash()) externTd := new(big.Int).Add(header.Difficulty, ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { // Delete any canonical number assignments above the new head for i := header.Number.Uint64() + 1; GetCanonicalHash(self.chainDb, i) != (common.Hash{}); i++ { DeleteCanonicalHash(self.chainDb, i) } // Overwrite any stale canonical number assignments head := self.GetHeader(header.ParentHash) for GetCanonicalHash(self.chainDb, head.Number.Uint64()) != head.Hash() { WriteCanonicalHash(self.chainDb, head.Hash(), head.Number.Uint64()) head = self.GetHeader(head.ParentHash) } // Extend the canonical chain with the new header if err := WriteCanonicalHash(self.chainDb, header.Hash(), header.Number.Uint64()); err != nil { glog.Fatalf("failed to insert header number: %v", err) } if err := WriteHeadHeaderHash(self.chainDb, header.Hash()); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } self.currentHeader = types.CopyHeader(header) } // Irrelevant of the canonical status, write the header itself to the database if err := WriteTd(self.chainDb, header.Hash(), externTd); err != nil { glog.Fatalf("failed to write header total difficulty: %v", err) } if err := WriteHeader(self.chainDb, header); err != nil { glog.Fatalf("filed to write header contents: %v", err) } return nil }
func (bc *ChainManager) setLastState() error { head := GetHeadBlockHash(bc.chainDb) if head != (common.Hash{}) { block := bc.GetBlock(head) if block != nil { bc.currentBlock = block } else { glog.Infof("LastBlock (%x) not found. Recovering...\n", head) if bc.recover() { glog.Infof("Recover successful") } else { glog.Fatalf("Recover failed. Please report") } } } else { bc.Reset() } bc.td = bc.GetTd(bc.currentBlock.Hash()) bc.currentGasLimit = CalcGasLimit(bc.currentBlock) if glog.V(logger.Info) { glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) } return nil }
// SetHead rewinds the local chain to a new head. Everything above the new head // will be deleted and the new one set. func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { height := uint64(0) if hc.currentHeader != nil { height = hc.currentHeader.Number.Uint64() } for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head { hash := hc.currentHeader.Hash() if delFn != nil { delFn(hash) } DeleteHeader(hc.chainDb, hash) DeleteTd(hc.chainDb, hash) hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash) } // Roll back the canonical chain numbering for i := height; i > head; i-- { DeleteCanonicalHash(hc.chainDb, i) } // Clear out any stale content from the caches hc.headerCache.Purge() hc.tdCache.Purge() if hc.currentHeader == nil { hc.currentHeader = hc.genesisHeader } hc.currentHeaderHash = hc.currentHeader.Hash() if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { glog.Fatalf("failed to reset head header hash: %v", err) } }
// SetCurrentHeader sets the current head header of the canonical chain. func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } hc.currentHeader = head hc.currentHeaderHash = head.Hash() }
// WriteHeadFastBlockHash stores the fast head block's hash. func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error { if err := db.Put(headFastKey, hash.Bytes()); err != nil { glog.Fatalf("failed to store last fast block's hash into database: %v", err) return err } return nil }
func (bc *ChainManager) setLastState() error { data, _ := bc.chainDb.Get([]byte("LastBlock")) if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) if block != nil { bc.currentBlock = block bc.lastBlockHash = block.Hash() } else { glog.Infof("LastBlock (%x) not found. Recovering...\n", data) if bc.recover() { glog.Infof("Recover successful") } else { glog.Fatalf("Recover failed. Please report") } } } else { bc.Reset() } bc.td = bc.currentBlock.Td bc.currentGasLimit = CalcGasLimit(bc.currentBlock) if glog.V(logger.Info) { glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) } return nil }
// WriteBlock writes the block to the chain. func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) { self.wg.Add(1) defer self.wg.Done() // Calculate the total difficulty of the block ptd := self.GetTd(block.ParentHash()) if ptd == nil { return NonStatTy, ParentError(block.ParentHash()) } td := new(big.Int).Add(block.Difficulty(), ptd) self.mu.RLock() cblock := self.currentBlock self.mu.RUnlock() // Compare the TD of the last known block in the canonical chain to make sure it's greater. // At this point it's possible that a different chain (fork) becomes the new canonical chain. if td.Cmp(self.Td()) > 0 { // chain fork if block.ParentHash() != cblock.Hash() { // during split we merge two different chains and create the new canonical chain err := self.reorg(cblock, block) if err != nil { return NonStatTy, err } } status = CanonStatTy self.mu.Lock() self.setTotalDifficulty(td) self.insert(block) self.mu.Unlock() } else { status = SideStatTy } if err := WriteTd(self.chainDb, block.Hash(), td); err != nil { glog.Fatalf("failed to write block total difficulty: %v", err) } if err := WriteBlock(self.chainDb, block); err != nil { glog.Fatalf("filed to write block contents: %v", err) } // Delete from future blocks self.futureBlocks.Remove(block.Hash()) return }
func (bc *ChainManager) recover() bool { data, _ := bc.chainDb.Get([]byte("checkpoint")) if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) if block != nil { if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { glog.Fatalf("failed to write database head number: %v", err) } if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to write database head hash: %v", err) } bc.currentBlock = block return true } } return false }
// WriteCanonicalHash stores the canonical hash for the given block number. func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error { key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...) if err := db.Put(key, hash.Bytes()); err != nil { glog.Fatalf("failed to store number to hash mapping into database: %v", err) return err } return nil }
// WriteBlock writes the block to the chain. func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err error) { self.wg.Add(1) defer self.wg.Done() // Calculate the total difficulty of the block ptd := self.GetTd(block.ParentHash()) if ptd == nil { return NonStatTy, ParentError(block.ParentHash()) } localTd := self.GetTd(self.currentBlock.Hash()) externTd := new(big.Int).Add(block.Difficulty(), ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { // Reorganise the chain if the parent is not the head block if block.ParentHash() != self.currentBlock.Hash() { if err := self.reorg(self.currentBlock, block); err != nil { return NonStatTy, err } } // Insert the block as the new head of the chain self.insert(block) status = CanonStatTy } else { status = SideStatTy } // Irrelevant of the canonical status, write the block itself to the database if err := self.hc.WriteTd(block.Hash(), externTd); err != nil { glog.Fatalf("failed to write block total difficulty: %v", err) } if err := WriteBlock(self.chainDb, block); err != nil { glog.Fatalf("failed to write block contents: %v", err) } self.futureBlocks.Remove(block.Hash()) return }
// writeHeader writes a header into the local chain, given that its parent is // already known. If the total difficulty of the newly inserted header becomes // greater than the current known TD, the canonical chain is re-routed. // // Note: This method is not concurrent-safe with inserting blocks simultaneously // into the chain, as side effects caused by reorganisations cannot be emulated // without the real blocks. Hence, writing headers directly should only be done // in two scenarios: pure-header mode of operation (light clients), or properly // separated header/block phases (non-archive clients). func (self *BlockChain) writeHeader(header *types.Header) error { self.wg.Add(1) defer self.wg.Done() // Calculate the total difficulty of the header ptd := self.GetTd(header.ParentHash) if ptd == nil { return ParentError(header.ParentHash) } td := new(big.Int).Add(header.Difficulty, ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain if td.Cmp(self.GetTd(self.currentHeader.Hash())) > 0 { // Delete any canonical number assignments above the new head for i := header.Number.Uint64() + 1; GetCanonicalHash(self.chainDb, i) != (common.Hash{}); i++ { DeleteCanonicalHash(self.chainDb, i) } // Overwrite any stale canonical number assignments head := self.GetHeader(header.ParentHash) for GetCanonicalHash(self.chainDb, head.Number.Uint64()) != head.Hash() { WriteCanonicalHash(self.chainDb, head.Hash(), head.Number.Uint64()) head = self.GetHeader(head.ParentHash) } // Extend the canonical chain with the new header if err := WriteCanonicalHash(self.chainDb, header.Hash(), header.Number.Uint64()); err != nil { glog.Fatalf("failed to insert header number: %v", err) } if err := WriteHeadHeaderHash(self.chainDb, header.Hash()); err != nil { glog.Fatalf("failed to insert head header hash: %v", err) } self.currentHeader = types.CopyHeader(header) } // Irrelevant of the canonical status, write the header itself to the database if err := WriteTd(self.chainDb, header.Hash(), td); err != nil { glog.Fatalf("failed to write header total difficulty: %v", err) } if err := WriteHeader(self.chainDb, header); err != nil { glog.Fatalf("filed to write header contents: %v", err) } return nil }
// insert injects a block into the current chain block chain. Note, this function // assumes that the `mu` mutex is held! func (bc *ChainManager) insert(block *types.Block) { // Add the block to the canonical chain number scheme and mark as the head if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { glog.Fatalf("failed to insert block number: %v", err) } if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { glog.Fatalf("failed to insert block number: %v", err) } // Add a new restore point if we reached some limit bc.checkpoint++ if bc.checkpoint > checkpointLimit { if err := bc.chainDb.Put([]byte("checkpoint"), block.Hash().Bytes()); err != nil { glog.Fatalf("failed to create checkpoint: %v", err) } bc.checkpoint = 0 } // Update the internal internal state with the head block bc.currentBlock = block }
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the // specified genesis state. func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) { // Dump the entire block chain and purge the caches bc.SetHead(0) bc.mu.Lock() defer bc.mu.Unlock() // Prepare the genesis block and reinitialise the chain if err := WriteTd(bc.chainDb, genesis.Hash(), genesis.Difficulty()); err != nil { glog.Fatalf("failed to write genesis block TD: %v", err) } if err := WriteBlock(bc.chainDb, genesis); err != nil { glog.Fatalf("failed to write genesis block: %v", err) } bc.genesisBlock = genesis bc.insert(bc.genesisBlock) bc.currentBlock = bc.genesisBlock bc.currentHeader = bc.genesisBlock.Header() bc.currentFastBlock = bc.genesisBlock }
// WriteBlock writes the block to the chain. func (self *BlockChain) WriteBlock(block *types.Block) (status writeStatus, err error) { self.wg.Add(1) defer self.wg.Done() // Calculate the total difficulty of the block ptd := self.GetTd(block.ParentHash()) if ptd == nil { return NonStatTy, ParentError(block.ParentHash()) } td := new(big.Int).Add(block.Difficulty(), ptd) // Make sure no inconsistent state is leaked during insertion self.mu.Lock() defer self.mu.Unlock() // If the total difficulty is higher than our known, add it to the canonical chain if td.Cmp(self.GetTd(self.currentBlock.Hash())) > 0 { // Reorganize the chain if the parent is not the head block if block.ParentHash() != self.currentBlock.Hash() { if err := self.reorg(self.currentBlock, block); err != nil { return NonStatTy, err } } // Insert the block as the new head of the chain self.insert(block) status = CanonStatTy } else { status = SideStatTy } // Irrelevant of the canonical status, write the block itself to the database if err := WriteTd(self.chainDb, block.Hash(), td); err != nil { glog.Fatalf("failed to write block total difficulty: %v", err) } if err := WriteBlock(self.chainDb, block); err != nil { glog.Fatalf("filed to write block contents: %v", err) } self.futureBlocks.Remove(block.Hash()) return }
// WriteTd serializes the total difficulty of a block into the database. func WriteTd(db ethdb.Database, hash common.Hash, td *big.Int) error { data, err := rlp.EncodeToBytes(td) if err != nil { return err } key := append(append(blockPrefix, hash.Bytes()...), tdSuffix...) if err := db.Put(key, data); err != nil { glog.Fatalf("failed to store block total difficulty into database: %v", err) return err } glog.V(logger.Debug).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td) return nil }
// WriteBody serializes the body of a block into the database. func WriteBody(db ethdb.Database, hash common.Hash, body *types.Body) error { data, err := rlp.EncodeToBytes(body) if err != nil { return err } key := append(append(blockPrefix, hash.Bytes()...), bodySuffix...) if err := db.Put(key, data); err != nil { glog.Fatalf("failed to store block body into database: %v", err) return err } glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4]) return nil }
// WriteHeader serializes a block header into the database. func WriteHeader(db ethdb.Database, header *types.Header) error { data, err := rlp.EncodeToBytes(header) if err != nil { return err } key := append(append(blockPrefix, header.Hash().Bytes()...), headerSuffix...) if err := db.Put(key, data); err != nil { glog.Fatalf("failed to store header into database: %v", err) return err } glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, header.Hash().Bytes()[:4]) return nil }
// SetHead rewinds the local chain to a new head. In the case of headers, everything // above the new head will be deleted and the new one set. In the case of blocks // though, the head may be further rewound if block bodies are missing (non-archive // nodes after a fast sync). func (bc *BlockChain) SetHead(head uint64) { bc.mu.Lock() defer bc.mu.Unlock() delFn := func(hash common.Hash) { DeleteBody(bc.chainDb, hash) } bc.hc.SetHead(head, delFn) // Clear out any stale content from the caches bc.bodyCache.Purge() bc.bodyRLPCache.Purge() bc.blockCache.Purge() bc.futureBlocks.Purge() // Update all computed fields to the new head if bc.currentBlock != nil && bc.hc.CurrentHeader().Number.Uint64() < bc.currentBlock.NumberU64() { bc.currentBlock = bc.GetBlock(bc.hc.CurrentHeader().Hash()) } if bc.currentFastBlock != nil && bc.hc.CurrentHeader().Number.Uint64() < bc.currentFastBlock.NumberU64() { bc.currentFastBlock = bc.GetBlock(bc.hc.CurrentHeader().Hash()) } if bc.currentBlock == nil { bc.currentBlock = bc.genesisBlock } if bc.currentFastBlock == nil { bc.currentFastBlock = bc.genesisBlock } if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { glog.Fatalf("failed to reset head block hash: %v", err) } if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { glog.Fatalf("failed to reset head fast block hash: %v", err) } bc.loadLastState() }
// WriteBlockReceipts stores all the transaction receipts belonging to a block // as a single receipt slice. This is used during chain reorganisations for // rescheduling dropped transactions. func WriteBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error { // Convert the receipts into their storage form and serialize them storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) for i, receipt := range receipts { storageReceipts[i] = (*types.ReceiptForStorage)(receipt) } bytes, err := rlp.EncodeToBytes(storageReceipts) if err != nil { return err } // Store the flattened receipt slice if err := db.Put(append(blockReceiptsPrefix, hash.Bytes()...), bytes); err != nil { glog.Fatalf("failed to store block receipts into database: %v", err) return err } glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4]) return nil }
func (bc *ChainManager) setLastState() { data, _ := bc.blockDb.Get([]byte("LastBlock")) if len(data) != 0 { block := bc.GetBlock(common.BytesToHash(data)) if block != nil { bc.currentBlock = block bc.lastBlockHash = block.Hash() } else { glog.Fatalf("Fatal. LastBlock not found. Please run removedb and resync") } } else { bc.Reset() } bc.td = bc.currentBlock.Td bc.currentGasLimit = CalcGasLimit(bc.currentBlock) if glog.V(logger.Info) { glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) } }
// WriteReceipts stores a batch of transaction receipts into the database. func WriteReceipts(db ethdb.Database, receipts types.Receipts) error { batch := db.NewBatch() // Iterate over all the receipts and queue them for database injection for _, receipt := range receipts { storageReceipt := (*types.ReceiptForStorage)(receipt) data, err := rlp.EncodeToBytes(storageReceipt) if err != nil { return err } if err := batch.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data); err != nil { return err } } // Write the scheduled data into the database if err := batch.Write(); err != nil { glog.Fatalf("failed to store receipts into database: %v", err) return err } return nil }
// WriteTransactions stores the transactions associated with a specific block // into the given database. Beside writing the transaction, the function also // stores a metadata entry along with the transaction, detailing the position // of this within the blockchain. func WriteTransactions(db ethdb.Database, block *types.Block) error { batch := db.NewBatch() // Iterate over each transaction and encode it with its metadata for i, tx := range block.Transactions() { // Encode and queue up the transaction for storage data, err := rlp.EncodeToBytes(tx) if err != nil { return err } if err := batch.Put(tx.Hash().Bytes(), data); err != nil { return err } // Encode and queue up the transaction metadata for storage meta := struct { BlockHash common.Hash BlockIndex uint64 Index uint64 }{ BlockHash: block.Hash(), BlockIndex: block.NumberU64(), Index: uint64(i), } data, err = rlp.EncodeToBytes(meta) if err != nil { return err } if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil { return err } } // Write the scheduled data into the database if err := batch.Write(); err != nil { glog.Fatalf("failed to store transactions into database: %v", err) return err } return nil }
// MakeEthConfig creates ethereum options from set command line flags. func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { customName := ctx.GlobalString(IdentityFlag.Name) if len(customName) > 0 { clientID += "/" + customName } am := MakeAccountManager(ctx) etherbase, err := ParamToAddress(ctx.GlobalString(EtherbaseFlag.Name), am) if err != nil { glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default") } // Assemble the entire eth configuration and return cfg := ð.Config{ Name: common.MakeName(clientID, version), DataDir: MustDataDir(ctx), GenesisFile: ctx.GlobalString(GenesisFileFlag.Name), FastSync: ctx.GlobalBool(FastSyncFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), DatabaseCache: ctx.GlobalInt(CacheFlag.Name), SkipBcVersionCheck: false, NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name), Verbosity: ctx.GlobalInt(VerbosityFlag.Name), Etherbase: common.HexToAddress(etherbase), MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), AccountManager: am, VmDebug: ctx.GlobalBool(VMDebugFlag.Name), MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name), MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name), Port: ctx.GlobalString(ListenPortFlag.Name), Olympic: ctx.GlobalBool(OlympicFlag.Name), NAT: MakeNAT(ctx), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), DocRoot: ctx.GlobalString(DocRootFlag.Name), Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name), NodeKey: MakeNodeKey(ctx), Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), Dial: true, BootNodes: ctx.GlobalString(BootnodesFlag.Name), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)), GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)), GpoFullBlockRatio: ctx.GlobalInt(GpoFullBlockRatioFlag.Name), GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name), GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name), GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name), SolcPath: ctx.GlobalString(SolcPathFlag.Name), AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), } if ctx.GlobalBool(DevModeFlag.Name) && ctx.GlobalBool(TestNetFlag.Name) { glog.Fatalf("%s and %s are mutually exclusive\n", DevModeFlag.Name, TestNetFlag.Name) } if ctx.GlobalBool(TestNetFlag.Name) { // testnet is always stored in the testnet folder cfg.DataDir += "/testnet" cfg.NetworkId = 2 cfg.TestNet = true } if ctx.GlobalBool(VMEnableJitFlag.Name) { cfg.Name += "/JIT" } if ctx.GlobalBool(DevModeFlag.Name) { if !ctx.GlobalIsSet(VMDebugFlag.Name) { cfg.VmDebug = true } if !ctx.GlobalIsSet(MaxPeersFlag.Name) { cfg.MaxPeers = 0 } if !ctx.GlobalIsSet(GasPriceFlag.Name) { cfg.GasPrice = new(big.Int) } if !ctx.GlobalIsSet(ListenPortFlag.Name) { cfg.Port = "0" // auto port } if !ctx.GlobalIsSet(WhisperEnabledFlag.Name) { cfg.Shh = true } if !ctx.GlobalIsSet(DataDirFlag.Name) { cfg.DataDir = os.TempDir() + "/ethereum_dev_mode" } cfg.PowTest = true cfg.DevMode = true glog.V(logger.Info).Infoln("dev mode enabled") } return cfg }
// SetHead rewinds the local chain to a new head. In the case of headers, everything // above the new head will be deleted and the new one set. In the case of blocks // though, the head may be further rewound if block bodies are missing (non-archive // nodes after a fast sync). func (bc *BlockChain) SetHead(head uint64) { bc.mu.Lock() defer bc.mu.Unlock() // Figure out the highest known canonical headers and/or blocks height := uint64(0) if bc.currentHeader != nil { if hh := bc.currentHeader.Number.Uint64(); hh > height { height = hh } } if bc.currentBlock != nil { if bh := bc.currentBlock.NumberU64(); bh > height { height = bh } } if bc.currentFastBlock != nil { if fbh := bc.currentFastBlock.NumberU64(); fbh > height { height = fbh } } // Gather all the hashes that need deletion drop := make(map[common.Hash]struct{}) for bc.currentHeader != nil && bc.currentHeader.Number.Uint64() > head { drop[bc.currentHeader.Hash()] = struct{}{} bc.currentHeader = bc.GetHeader(bc.currentHeader.ParentHash) } for bc.currentBlock != nil && bc.currentBlock.NumberU64() > head { drop[bc.currentBlock.Hash()] = struct{}{} bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash()) } for bc.currentFastBlock != nil && bc.currentFastBlock.NumberU64() > head { drop[bc.currentFastBlock.Hash()] = struct{}{} bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash()) } // Roll back the canonical chain numbering for i := height; i > head; i-- { DeleteCanonicalHash(bc.chainDb, i) } // Delete everything found by the above rewind for hash, _ := range drop { DeleteHeader(bc.chainDb, hash) DeleteBody(bc.chainDb, hash) DeleteTd(bc.chainDb, hash) } // Clear out any stale content from the caches bc.headerCache.Purge() bc.bodyCache.Purge() bc.bodyRLPCache.Purge() bc.blockCache.Purge() bc.futureBlocks.Purge() // Update all computed fields to the new head if bc.currentBlock == nil { bc.currentBlock = bc.genesisBlock } if bc.currentHeader == nil { bc.currentHeader = bc.genesisBlock.Header() } if bc.currentFastBlock == nil { bc.currentFastBlock = bc.genesisBlock } if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { glog.Fatalf("failed to reset head block hash: %v", err) } if err := WriteHeadHeaderHash(bc.chainDb, bc.currentHeader.Hash()); err != nil { glog.Fatalf("failed to reset head header hash: %v", err) } if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { glog.Fatalf("failed to reset head fast block hash: %v", err) } bc.loadLastState() }
// InsertReceiptChain attempts to complete an already existing header chain with // transaction and receipt data. func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { self.wg.Add(1) defer self.wg.Done() // Collect some import statistics to report on stats := struct{ processed, ignored int32 }{} start := time.Now() // Create the block importing task queue and worker functions tasks := make(chan int, len(blockChain)) for i := 0; i < len(blockChain) && i < len(receiptChain); i++ { tasks <- i } close(tasks) errs, failed := make([]error, len(tasks)), int32(0) process := func(worker int) { for index := range tasks { block, receipts := blockChain[index], receiptChain[index] // Short circuit insertion if shutting down or processing failed if atomic.LoadInt32(&self.procInterrupt) == 1 { return } if atomic.LoadInt32(&failed) > 0 { return } // Short circuit if the owner header is unknown if !self.HasHeader(block.Hash()) { errs[index] = fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4]) atomic.AddInt32(&failed, 1) return } // Skip if the entire data is already known if self.HasBlock(block.Hash()) { atomic.AddInt32(&stats.ignored, 1) continue } // Compute all the non-consensus fields of the receipts transactions, logIndex := block.Transactions(), uint(0) for j := 0; j < len(receipts); j++ { // The transaction hash can be retrieved from the transaction itself receipts[j].TxHash = transactions[j].Hash() // The contract address can be derived from the transaction itself if MessageCreatesContract(transactions[j]) { from, _ := transactions[j].From() receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce()) } // The used gas can be calculated based on previous receipts if j == 0 { receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed) } else { receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed) } // The derived log fields can simply be set from the block and transaction for k := 0; k < len(receipts[j].Logs); k++ { receipts[j].Logs[k].BlockNumber = block.NumberU64() receipts[j].Logs[k].BlockHash = block.Hash() receipts[j].Logs[k].TxHash = receipts[j].TxHash receipts[j].Logs[k].TxIndex = uint(j) receipts[j].Logs[k].Index = logIndex logIndex++ } } // Write all the data out into the database if err := WriteBody(self.chainDb, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { errs[index] = fmt.Errorf("failed to write block body: %v", err) atomic.AddInt32(&failed, 1) glog.Fatal(errs[index]) return } if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { errs[index] = fmt.Errorf("failed to write block receipts: %v", err) atomic.AddInt32(&failed, 1) glog.Fatal(errs[index]) return } if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil { errs[index] = fmt.Errorf("failed to write log blooms: %v", err) atomic.AddInt32(&failed, 1) glog.Fatal(errs[index]) return } atomic.AddInt32(&stats.processed, 1) } } // Start as many worker threads as goroutines allowed pending := new(sync.WaitGroup) for i := 0; i < runtime.GOMAXPROCS(0); i++ { pending.Add(1) go func(id int) { defer pending.Done() process(id) }(i) } pending.Wait() // If anything failed, report if failed > 0 { for i, err := range errs { if err != nil { return i, err } } } if atomic.LoadInt32(&self.procInterrupt) == 1 { glog.V(logger.Debug).Infoln("premature abort during receipt chain processing") return 0, nil } // Update the head fast sync block if better self.mu.Lock() head := blockChain[len(errs)-1] if self.GetTd(self.currentFastBlock.Hash()).Cmp(self.GetTd(head.Hash())) < 0 { if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil { glog.Fatalf("failed to update head fast block hash: %v", err) } self.currentFastBlock = head } self.mu.Unlock() // Report some public statistics so the user has a clue what's going on first, last := blockChain[0], blockChain[len(blockChain)-1] glog.V(logger.Info).Infof("imported %d receipt(s) (%d ignored) in %v. #%d [%x… / %x…]", stats.processed, stats.ignored, time.Since(start), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4]) return 0, nil }