private void putInternal(Block block, StatusContext ctx) { long t1 = System.currentTimeMillis(); Sha256Hash hash = block.getHash(); ctx.setStatus("BLOCK_CHECK_EXIST"); if (file_db.getBlockMap().containsKey(hash)) { imported_blocks.incrementAndGet(); return; } // Mark block as in progress Semaphore block_wait_sem; synchronized (in_progress) { block_wait_sem = in_progress.get(hash); if (block_wait_sem == null) { block_wait_sem = new Semaphore(0); in_progress.put(hash, block_wait_sem); } } // Kick off threaded storage of transactions int size = 0; ctx.setStatus("BLOCK_TX_CACHE_INSERT"); synchronized (transaction_cache) { for (Transaction tx : block.getTransactions()) { transaction_cache.put(tx.getHash(), SerializedTransaction.scrubTransaction(params, tx)); } } ctx.setStatus("BLOCK_TX_ENQUE"); LinkedList<Sha256Hash> tx_list = new LinkedList<Sha256Hash>(); Collection<Map.Entry<String, Sha256Hash>> addrTxLst = new LinkedList<Map.Entry<String, Sha256Hash>>(); Map<Sha256Hash, SerializedTransaction> txs_map = new HashMap<Sha256Hash, SerializedTransaction>(); for (Transaction tx : block.getTransactions()) { imported_transactions.incrementAndGet(); Collection<String> addrs = getAllAddresses(tx, true); for (String addr : addrs) { addrTxLst.add( new java.util.AbstractMap.SimpleEntry<String, Sha256Hash>(addr, tx.getHash())); } txs_map.put(tx.getHash(), new SerializedTransaction(tx)); tx_list.add(tx.getHash()); size++; } ctx.setStatus("TX_SAVEALL"); file_db.getTransactionMap().putAll(txs_map); ctx.setStatus("BLOCK_TX_MAP_ADD"); file_db.addTxsToBlockMap(tx_list, hash); ctx.setStatus("ADDR_SAVEALL"); file_db.addAddressesToTxMap(addrTxLst); int h = block_store.getHeight(hash); for (Transaction tx : block.getTransactions()) { Collection<String> addrs = getAllAddresses(tx, true); ctx.setStatus("TX_NOTIFY"); jelly.getElectrumNotifier().notifyNewTransaction(tx, addrs, h); ctx.setStatus("TX_DONE"); } // Once all transactions are in, check for prev block in this store ctx.setStatus("BLOCK_WAIT_PREV"); Sha256Hash prev_hash = block.getPrevBlockHash(); waitForBlockStored(prev_hash); // System.out.println("Block " + hash + " " + Util.measureSerialization(new // SerializedBlock(block))); ctx.setStatus("BLOCK_SAVE"); file_db.getBlockMap().put(hash, new SerializedBlock(block)); block_wait_sem.release(1024); boolean wait_for_utxo = false; if (jelly.isUpToDate() && jelly.getUtxoTrieMgr().isUpToDate()) { wait_for_utxo = true; } jelly.getUtxoTrieMgr().notifyBlock(wait_for_utxo); if (wait_for_utxo) { jelly.getEventLog().alarm("UTXO root hash: " + jelly.getUtxoTrieMgr().getRootHash()); } jelly.getElectrumNotifier().notifyNewBlock(block); long t2 = System.currentTimeMillis(); DecimalFormat df = new DecimalFormat("0.000"); double sec = (t2 - t1) / 1000.0; if (h % block_print_every == 0) { jelly .getEventLog() .alarm( "Saved block: " + hash + " - " + h + " - " + size + " (" + df.format(sec) + " seconds)"); } jelly .getEventLog() .log( "Saved block: " + hash + " - " + h + " - " + size + " (" + df.format(sec) + " seconds)"); imported_blocks.incrementAndGet(); }
private void load(File file) throws IOException, BlockStoreException { log.info("Reading block store from {}", file); InputStream input = null; try { input = new BufferedInputStream(new FileInputStream(file)); // Read a version byte. int version = input.read(); if (version == -1) { // No such file or the file was empty. throw new FileNotFoundException(file.getName() + " does not exist or is empty"); } if (version != 1) { throw new BlockStoreException("Bad version number: " + version); } // Chain head pointer is the first thing in the file. byte[] chainHeadHash = new byte[32]; if (input.read(chainHeadHash) < chainHeadHash.length) throw new BlockStoreException("Truncated block store: cannot read chain head hash"); this.chainHead = new Sha256Hash(chainHeadHash); log.info("Read chain head from disk: {}", this.chainHead); long now = System.currentTimeMillis(); // Rest of file is raw block headers. byte[] headerBytes = new byte[Block.HEADER_SIZE]; try { while (true) { // Read a block from disk. if (input.read(headerBytes) < 80) { // End of file. break; } // Parse it. Block b = new Block(params, headerBytes); // Look up the previous block it connects to. StoredBlock prev = get(b.getPrevBlockHash()); StoredBlock s; if (prev == null) { // First block in the stored chain has to be treated specially. if (b.equals(params.genesisBlock)) { s = new StoredBlock( params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0); } else { throw new BlockStoreException( "Could not connect " + b.getHash().toString() + " to " + b.getPrevBlockHash().toString()); } } else { // Don't try to verify the genesis block to avoid upsetting the unit tests. b.verifyHeader(); // Calculate its height and total chain work. s = prev.build(b); } // Save in memory. blockMap.put(b.getHash(), s); } } catch (ProtocolException e) { // Corrupted file. throw new BlockStoreException(e); } catch (VerificationException e) { // Should not be able to happen unless the file contains bad blocks. throw new BlockStoreException(e); } long elapsed = System.currentTimeMillis() - now; log.info("Block chain read complete in {}ms", elapsed); } finally { if (input != null) input.close(); } }