Skip to content

Commit 580d312

Browse files
committed
block index reader
1 parent 54d9e8f commit 580d312

14 files changed

+565
-100
lines changed

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
],
1616
"require": {
1717
"php": "^7.1",
18-
"andkom/bcdatastream": "^1.0",
18+
"ext-leveldb": "^0.2.1",
1919
"stephenhill/base58": "^1.1",
20-
"bitwasp/bech32": "^0.0.1"
20+
"bitwasp/bech32": "^0.0.1",
21+
"andkom/php-bcdatastream": "^1.1"
2122
},
2223
"require-dev": {
2324
"phpunit/phpunit": ">=5.0"

examples/print_balances.php

Lines changed: 0 additions & 63 deletions
This file was deleted.

examples/read_blockchain.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
require_once '../vendor/autoload.php';
4+
5+
$dataDir = getenv('HOME') . '/Library/Application Support/Bitcoin';
6+
7+
$reader = new \AndKom\PhpBitcoinBlockchain\BlockchainReader($dataDir);
8+
9+
foreach ($reader->readBlocks(0, 100) as $height => $block) {
10+
echo $height . " => " . $block->header->getHash() . "\n";
11+
}
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22

33
include '../vendor/autoload.php';
44

5-
$blockDir = getenv('HOME') . '/Library/Application Support/Bitcoin/blocks';
5+
$dataDir = getenv('HOME') . '/Library/Application Support/Bitcoin';
6+
$blocksDir = "$dataDir/blocks";
7+
$blockFile = "$blocksDir/blk00001.dat";
68

79
$reader = new \AndKom\PhpBitcoinBlockchain\BlockFileReader();
810

9-
foreach ($reader->read($blockDir . '/blk00000.dat') as $block) {
10-
11-
echo str_repeat('=', 71) . "\n";
12-
echo "Block: " . $block->header->getHash() . "\n";
13-
echo "Date: " . date('r', $block->header->time) . "\n";
14-
11+
foreach ($reader->read($blockFile) as $block) {
1512
foreach ($block->transactions as $tx) {
1613
echo "\nTX: " . $tx->getHash() . "\n";
1714

examples/read_index.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
require_once '../vendor/autoload.php';
4+
5+
$dataDir = getenv('HOME') . '/Library/Application Support/Bitcoin';
6+
$blocksDir = "$dataDir/blocks";
7+
$indexDir = "$blocksDir/index";
8+
9+
$reader = new \AndKom\PhpBitcoinBlockchain\IndexReader($indexDir);
10+
11+
echo "Reading block index...\n";
12+
13+
$index = $reader->read();
14+
15+
echo 'Last file: ' . $index->getLastFile() . "\n";
16+
echo 'Min height: ' . $index->getMinHeight() . "\n";
17+
echo 'Max height: ' . $index->getMaxHeight() . "\n";
18+
19+
var_dump($index->getFileInfo(0));
20+
21+
var_dump($index->getBlockInfoByHeight(0));

src/Block.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static public function parse(Reader $stream): self
3636
{
3737
$block = new self;
3838
$block->header = Header::parse($stream);
39-
$block->transactionsCount = $stream->readVarInt();
39+
$block->transactionsCount = $stream->readCompactSize();
4040

4141
for ($i = 0; $i < $block->transactionsCount; $i++) {
4242
$block->transactions[] = Transaction::parse($stream);
@@ -52,7 +52,7 @@ static public function parse(Reader $stream): self
5252
public function serialize(Writer $stream): self
5353
{
5454
$this->header->serialize($stream);
55-
$stream->writeVarInt(count($this->transactions));
55+
$stream->writeCompactSize(count($this->transactions));
5656

5757
foreach ($this->transactions as $transaction) {
5858
$transaction->serialize($stream);

src/BlockFileReader.php

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
*/
1313
class BlockFileReader
1414
{
15+
const MAGIC = "\xf9\xbe\xb4\xd9";
16+
1517
/**
1618
* @param string $file
1719
* @return \Generator
@@ -22,38 +24,41 @@ public function read(string $file): \Generator
2224
$fp = fopen($file, 'r');
2325

2426
if (!$fp) {
25-
throw new Exception("Unable to open file $file for reading.");
27+
throw new Exception("Unable to open block file '$file' for reading.");
2628
}
2729

28-
while (true) {
29-
$magic = fread($fp, 4);
30-
31-
if (!$magic) {
32-
break;
33-
}
30+
while (fread($fp, 4) == static::MAGIC) {
31+
yield $this->readBlock($fp);
32+
}
3433

35-
if ($magic != "\xf9\xbe\xb4\xd9") {
36-
throw new Exception('Invalid magic number.');
37-
}
34+
fclose($fp);
35+
}
3836

39-
$size = fread($fp, 4);
37+
/**
38+
* @param resource $fp
39+
* @return Block
40+
* @throws Exception
41+
*/
42+
public function readBlock($fp): Block
43+
{
44+
if (!is_resource($fp)) {
45+
throw new Exception('Invalid file resource.');
46+
}
4047

41-
if ($size === false) {
42-
throw new Exception('Unable to read block size.');
43-
}
48+
$size = fread($fp, 4);
4449

45-
$length = unpack('V', $size)[1];
46-
$data = fread($fp, $length);
50+
if ($size === false) {
51+
throw new Exception('Unable to read block size.');
52+
}
4753

48-
if ($data === false) {
49-
throw new Exception('Unable to read block data.');
50-
}
54+
$length = unpack('V', $size)[1];
5155

52-
$stream = new Reader($data);
56+
$data = fread($fp, $length);
5357

54-
yield Block::parse($stream);
58+
if ($data === false) {
59+
throw new Exception('Unable to read block data.');
5560
}
5661

57-
fclose($fp);
62+
return Block::parse(new Reader($data));
5863
}
5964
}

src/BlockInfo.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AndKom\PhpBitcoinBlockchain;
6+
7+
use AndKom\BCDataStream\Reader;
8+
9+
/**
10+
* Class BlockInfo
11+
* @package AndKom\PhpBitcoinBlockchain
12+
*/
13+
class BlockInfo
14+
{
15+
const BLOCK_VALID_UNKNOWN = 0;
16+
const BLOCK_VALID_HEADER = 1;
17+
const BLOCK_VALID_TREE = 2;
18+
const BLOCK_VALID_TRANSACTIONS = 3;
19+
const BLOCK_VALID_CHAIN = 4;
20+
const BLOCK_VALID_SCRIPTS = 5;
21+
const BLOCK_VALID_MASK = self::BLOCK_VALID_HEADER | self::BLOCK_VALID_TREE | self::BLOCK_VALID_TRANSACTIONS | self::BLOCK_VALID_CHAIN | self:: BLOCK_VALID_SCRIPTS;
22+
23+
const BLOCK_HAVE_DATA = 8;
24+
const BLOCK_HAVE_UNDO = 16;
25+
const BLOCK_HAVE_MASK = self::BLOCK_HAVE_DATA | self::BLOCK_HAVE_UNDO;
26+
27+
const BLOCK_FAILED_VALID = 32;
28+
const BLOCK_FAILED_CHILD = 64;
29+
const BLOCK_FAILED_MASK = self::BLOCK_FAILED_VALID | self::BLOCK_FAILED_CHILD;
30+
31+
const BLOCK_OPT_WITNESS = 128;
32+
33+
/**
34+
* @var int
35+
*/
36+
public $version;
37+
38+
/**
39+
* @var int
40+
*/
41+
public $height;
42+
43+
/**
44+
* @var int
45+
*/
46+
public $status;
47+
48+
/**
49+
* @var int
50+
*/
51+
public $txCount;
52+
53+
/**
54+
* @var int
55+
*/
56+
public $file;
57+
58+
/**
59+
* @var int
60+
*/
61+
public $dataPos;
62+
63+
/**
64+
* @var int
65+
*/
66+
public $undoPos;
67+
68+
/**
69+
* @var Header
70+
*/
71+
public $header;
72+
73+
/**
74+
* @param Reader $reader
75+
* @return BlockInfo
76+
*/
77+
static public function parse(Reader $reader): self
78+
{
79+
$block = new self;
80+
$block->version = $reader->readVarInt();
81+
$block->height = $reader->readVarInt();
82+
$block->status = $reader->readVarInt();
83+
$block->txCount = $reader->readVarInt();
84+
85+
if ($block->status & (static::BLOCK_HAVE_DATA | static::BLOCK_HAVE_UNDO)) {
86+
$block->file = $reader->readVarInt();
87+
}
88+
89+
if ($block->status & static::BLOCK_HAVE_DATA) {
90+
$block->dataPos = $reader->readVarInt();
91+
}
92+
93+
if ($block->status & static::BLOCK_HAVE_UNDO) {
94+
$block->undoPos = $reader->readVarInt();
95+
}
96+
97+
$block->header = Header::parse($reader);
98+
99+
return $block;
100+
}
101+
102+
/**
103+
* @return string
104+
* @throws Exception
105+
*/
106+
public function getFileName(): string
107+
{
108+
if (is_null($this->file)) {
109+
throw new Exception('Unknown block file number.');
110+
}
111+
112+
return sprintf('blk%05d.dat', $this->file);
113+
}
114+
}

0 commit comments

Comments
 (0)