Move BlockImporter to a util and make it an Iterator<Block>

This commit is contained in:
Matt Corallo 2013-04-25 13:33:35 -04:00
parent 1dadb33c28
commit a131d08a58
2 changed files with 167 additions and 102 deletions

View file

@ -0,0 +1,167 @@
/*
* Copyright 2012 Matt Corallo.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.utils;
import com.google.bitcoin.core.Block;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.ProtocolException;
import com.google.bitcoin.core.Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* <p>This class reads block files stored in the reference/Satoshi client format. This is simply a way to concatenate
* blocks together. Importing block data with this tool can be a lot faster than syncing over the network, if you
* have the files available.</p>
*
* <p>In order to comply with Iterator&lt;Block>, this class swallows a lot of IOExceptions, which may result in a few
* blocks being missed followed by a huge set of orphan blocks.</p>
*
* <p>To blindly import all files which can be found in a reference client (version >= 0.8) datadir automatically,
* try this code fragment:<br>
* BlockFileLoader loader = new BlockFileLoader(BlockFileLoader.getReferenceClientBlockFileList());<br>
* for (Block block : loader) {<br>
* &nbsp;&nbsp;try { chain.add(block); } catch (Exception e) { }<br>
* }</p>
*/
public class BlockFileLoader implements Iterator<Block> {
/**
* Gets the list of files which contain blocks from the Satoshi client.
*/
public static List<File> getReferenceClientBlockFileList() {
String defaultDataDir;
if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
defaultDataDir = System.getenv("APPDATA") + "\\.bitcoin\\blocks\\";
} else {
defaultDataDir = System.getProperty("user.home") + "/.bitcoin/blocks/";
}
List<File> list = new LinkedList<File>();
for (int i = 0; true; i++) {
File file = new File(defaultDataDir + String.format("blk%05d.dat", i));
if (!file.exists())
break;
list.add(file);
}
return list;
}
private Iterator<File> fileIt;
private FileInputStream currentFileStream = null;
private Block nextBlock = null;
private NetworkParameters params;
public BlockFileLoader(NetworkParameters params, List<File> files) {
fileIt = files.iterator();
this.params = params;
}
@Override
public boolean hasNext() {
if (nextBlock == null)
loadNextBlock();
return nextBlock != null;
}
@Override
public Block next() throws NoSuchElementException {
if (!hasNext())
throw new NoSuchElementException();
Block next = nextBlock;
nextBlock = null;
return next;
}
private void loadNextBlock() {
while (true) {
try {
if (!fileIt.hasNext() && (currentFileStream == null || currentFileStream.available() < 1))
break;
} catch (IOException e) {
currentFileStream = null;
if (!fileIt.hasNext())
break;
}
while (true) {
try {
if (currentFileStream != null && currentFileStream.available() > 0)
break;
} catch (IOException e1) {
currentFileStream = null;
}
if (!fileIt.hasNext()) {
nextBlock = null;
currentFileStream = null;
return;
}
try {
currentFileStream = new FileInputStream(fileIt.next());
} catch (FileNotFoundException e) {
currentFileStream = null;
}
}
try {
int nextChar = currentFileStream.read();
while (nextChar != -1) {
if (nextChar != ((params.packetMagic >>> 24) & 0xff)) {
nextChar = currentFileStream.read();
continue;
}
nextChar = currentFileStream.read();
if (nextChar != ((params.packetMagic >>> 16) & 0xff))
continue;
nextChar = currentFileStream.read();
if (nextChar != ((params.packetMagic >>> 8) & 0xff))
continue;
nextChar = currentFileStream.read();
if (nextChar == (params.packetMagic & 0xff))
break;
}
byte[] bytes = new byte[4];
currentFileStream.read(bytes, 0, 4);
long size = Utils.readUint32BE(Utils.reverseBytes(bytes), 0);
// We allow larger than MAX_BLOCK_SIZE because test code uses this as well.
if (size > Block.MAX_BLOCK_SIZE*2 || size <= 0)
continue;
bytes = new byte[(int) size];
currentFileStream.read(bytes, 0, (int) size);
try {
nextBlock = new Block(params, bytes);
} catch (ProtocolException e) {
nextBlock = null;
continue;
}
break;
} catch (IOException e) {
currentFileStream = null;
continue;
}
}
}
@Override
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
}

View file

@ -1,102 +0,0 @@
/*
* Copyright 2012 Matt Corallo.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.tools;
import com.google.bitcoin.core.Block;
import com.google.bitcoin.core.FullPrunedBlockChain;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.Utils;
import com.google.bitcoin.store.FullPrunedBlockStore;
import com.google.bitcoin.store.H2FullPrunedBlockStore;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* This class reads block files stored in the reference/Satoshi clients format. This is simply a way to concatenate
* blocks together. Importing block data with this tool can be a lot faster than syncing over the network, if you
* have the files available.
*/
public class BlockImporter {
public static void main(String[] args) throws Exception {
NetworkParameters params = NetworkParameters.prodNet();
FullPrunedBlockStore store = new H2FullPrunedBlockStore(params, "toy-full.blockchain", 100);
FullPrunedBlockChain chain = new FullPrunedBlockChain(params, store);
String defaultDataDir;
if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
defaultDataDir = System.getenv("APPDATA") + "\\.bitcoin\\blocks\\";
} else {
defaultDataDir = System.getProperty("user.home") + "/.bitcoin/blocks/";
}
// TODO: Move this to a library function
int i = 0;
for (int j = 0; true; j++) {
FileInputStream stream;
System.out.println("Opening " + defaultDataDir + String.format("blk%05d.dat", j));
try {
stream = new FileInputStream(new File(
defaultDataDir + String.format("blk%05d.dat", j)));
} catch (FileNotFoundException e1) {
System.out.println(defaultDataDir + String.format("blk%05d.dat", j));
break;
}
while (stream.available() > 0) {
try {
int nextChar = stream.read();
while (nextChar != -1) {
if (nextChar != ((params.packetMagic >>> 24) & 0xff)) {
nextChar = stream.read();
continue;
}
nextChar = stream.read();
if (nextChar != ((params.packetMagic >>> 16) & 0xff))
continue;
nextChar = stream.read();
if (nextChar != ((params.packetMagic >>> 8) & 0xff))
continue;
nextChar = stream.read();
if (nextChar == (params.packetMagic & 0xff))
break;
}
} catch (IOException e) {
break;
}
byte[] bytes = new byte[4];
stream.read(bytes, 0, 4);
long size = Utils.readUint32BE(Utils.reverseBytes(bytes), 0);
if (size > Block.MAX_BLOCK_SIZE || size <= 0)
continue;
bytes = new byte[(int) size];
stream.read(bytes, 0, (int) size);
Block block = new Block(params, bytes);
if (store.get(block.getHash()) == null)
chain.add(block);
if (i % 1000 == 0)
System.out.println(i);
i++;
}
stream.close();
}
System.out.println("Imported " + chain.getChainHead().getHeight() + " blocks.");
System.exit(0);
}
}