예제 #1
0
 public synchronized void put(StoredBlock block) throws BlockStoreException {
   try {
     Sha256Hash hash = block.getHeader().getHash();
     assert blockMap.get(hash) == null : "Attempt to insert duplicate";
     // Append to the end of the file. The other fields in StoredBlock will be recalculated when
     // it's reloaded.
     byte[] bytes = block.getHeader().bitcoinSerialize();
     stream.write(bytes);
     stream.flush();
     blockMap.put(hash, block);
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
예제 #2
0
 public synchronized void setChainHead(StoredBlock chainHead) throws BlockStoreException {
   try {
     this.chainHead = chainHead.getHeader().getHash();
     // Write out new hash to the first 32 bytes of the file past one (first byte is version
     // number).
     stream.getChannel().write(ByteBuffer.wrap(this.chainHead.getBytes()), 1);
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
예제 #3
0
  @Override
  public void put(StoredBlock block) throws BlockStoreException {
    final MappedByteBuffer buffer = this.buffer;
    if (buffer == null) throw new BlockStoreException("Store closed");

    lock.lock();
    try {
      int cursor = getRingCursor(buffer);
      if (cursor == getFileSize()) {
        // Wrapped around.
        cursor = FILE_PROLOGUE_BYTES;
      }
      buffer.position(cursor);
      Sha256Hash hash = block.getHeader().getHash();
      notFoundCache.remove(hash);
      buffer.put(hash.getBytes());
      block.serializeCompact(buffer);
      setRingCursor(buffer, buffer.position());
      blockCache.put(hash, block);
    } finally {
      lock.unlock();
    }
  }
예제 #4
0
  @Override
  public void setChainHead(StoredBlock chainHead) throws BlockStoreException {
    final MappedByteBuffer buffer = this.buffer;
    if (buffer == null) throw new BlockStoreException("Store closed");

    lock.lock();
    try {
      lastChainHead = chainHead;
      byte[] headHash = chainHead.getHeader().getHash().getBytes();
      buffer.position(8);
      buffer.put(headHash);
    } finally {
      lock.unlock();
    }
  }
예제 #5
0
 private void createNewStore(NetworkParameters params, File file) throws BlockStoreException {
   // Create a new block store if the file wasn't found or anything went wrong whilst reading.
   blockMap.clear();
   try {
     stream = new FileOutputStream(file, false); // Do not append, create fresh.
     stream.write(1); // Version.
   } catch (IOException e1) {
     // We could not load a block store nor could we create a new one!
     throw new BlockStoreException(e1);
   }
   try {
     // Set up the genesis block. When we start out fresh, it is by definition the top of the
     // chain.
     Block genesis = params.genesisBlock.cloneAsHeader();
     StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0);
     this.chainHead = storedGenesis.getHeader().getHash();
     stream.write(this.chainHead.getBytes());
     put(storedGenesis);
   } catch (VerificationException e1) {
     throw new RuntimeException(e1); // Cannot happen.
   } catch (IOException e) {
     throw new BlockStoreException(e);
   }
 }
예제 #6
0
  @Override
  @Nullable
  public StoredBlock get(Sha256Hash hash) throws BlockStoreException {
    final MappedByteBuffer buffer = this.buffer;
    if (buffer == null) throw new BlockStoreException("Store closed");

    lock.lock();
    try {
      StoredBlock cacheHit = blockCache.get(hash);
      if (cacheHit != null) return cacheHit;
      if (notFoundCache.get(hash) != null) return null;

      // Starting from the current tip of the ring work backwards until we have either found the
      // block or
      // wrapped around.
      int cursor = getRingCursor(buffer);
      final int startingPoint = cursor;
      final int fileSize = getFileSize();
      final byte[] targetHashBytes = hash.getBytes();
      byte[] scratch = new byte[32];
      do {
        cursor -= RECORD_SIZE;
        if (cursor < FILE_PROLOGUE_BYTES) {
          // We hit the start, so wrap around.
          cursor = fileSize - RECORD_SIZE;
        }
        // Cursor is now at the start of the next record to check, so read the hash and compare it.
        buffer.position(cursor);
        buffer.get(scratch);
        if (Arrays.equals(scratch, targetHashBytes)) {
          // Found the target.
          StoredBlock storedBlock = StoredBlock.deserializeCompact(params, buffer);
          blockCache.put(hash, storedBlock);
          return storedBlock;
        }
      } while (cursor != startingPoint);
      // Not found.
      notFoundCache.put(hash, notFoundMarker);
      return null;
    } catch (ProtocolException e) {
      throw new RuntimeException(e); // Cannot happen.
    } finally {
      lock.unlock();
    }
  }
예제 #7
0
 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();
   }
 }