Skip to content

Commit a062e72

Browse files
committed
refactoring
1 parent fca16d9 commit a062e72

File tree

12 files changed

+134
-77
lines changed

12 files changed

+134
-77
lines changed

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
A PHP implementation of Bitcoin blockchain database parser.
44

5-
Features:
5+
### Features:
66

77
- Parse unordered block data
8-
- Parse orderered block data
8+
- Parse ordered block data
99
- Parse block index
1010
- Parse chain state (UTXO database)
1111

@@ -38,6 +38,15 @@ foreach ($blockchainReader->readBlocksUnordered() as $block) {
3838
// read UTXO
3939
foreach ($blockchainReader->getChainState()->read() as $utxo) {
4040
}
41+
42+
// get block by hash
43+
$block = $blockchainReader->getBlockByHash('binary hash in little endian');
44+
45+
// get block by height
46+
$block = $blockchainReader->getBlockByHash(12345);
47+
48+
// get best block hash
49+
$hash = $blockchainReader->getChainState()->getBestBlock();
4150
```
4251

4352
See more examples in the examples dir.
@@ -47,15 +56,15 @@ See more examples in the examples dir.
4756
Ubuntu/Debian:
4857

4958
```bash
50-
# apt-get install libleveldb-dev
51-
# pecl install leveldb-0.2.1
59+
apt-get install libleveldb-dev
60+
pecl install leveldb-0.2.1
5261
```
5362

5463
Mac OS:
5564

5665
```bash
57-
# brew install leveldb
58-
# pecl install leveldb-0.2.1
66+
brew install leveldb
67+
pecl install leveldb-0.2.1
5968
```
6069

6170
Or compile from source:

examples/read_blockchain.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
$reader = new \AndKom\PhpBitcoinBlockchain\BlockchainReader($dataDir);
88

99
foreach ($reader->readBlocks(0, 100) as $height => $block) {
10-
echo $height . " => " . $block->header->getHash() . "\n";
10+
echo $height . " => " . \AndKom\PhpBitcoinBlockchain\Utils::hashToHex($block->header->getHash()) . "\n";
1111
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
foreach ($reader->read($blockFile) as $block) {
1212
foreach ($block->transactions as $tx) {
13-
echo "\nTX: " . $tx->getHash() . "\n";
13+
echo "\nTX: " . \AndKom\PhpBitcoinBlockchain\Utils::hashToHex($tx->getHash()) . "\n";
1414

1515
foreach ($tx->inputs as $in) {
1616
if ($in->isCoinbase()) {
1717
echo "IN: Coinbase\n";
1818
} else {
19-
echo "IN: " . $in->prevTxHash . ':' . $in->prevTxOutIndex . "\n";
19+
echo "IN: " . \AndKom\PhpBitcoinBlockchain\Utils::hashToHex($in->prevTxHash) . ':' . $in->prevTxOutIndex . "\n";
2020
}
2121
}
2222

examples/read_chainstate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
$reader = new \AndKom\PhpBitcoinBlockchain\ChainStateReader($chainStateDir);
99

1010
foreach ($reader->read() as $unspentOutput) {
11-
echo "TX: " . bin2hex(strrev($unspentOutput->hash)) . "\n";
11+
echo "TX: " . \AndKom\PhpBitcoinBlockchain\Utils::hashToHex($unspentOutput->hash) . "\n";
1212
echo "Index: " . $unspentOutput->index . "\n";
1313
echo "Height: " . $unspentOutput->height . "\n";
1414
echo "Coinbase: " . $unspentOutput->coinbase . "\n";

src/BlockFileReader.php

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,21 @@ class BlockFileReader
1515
const MAGIC = "\xf9\xbe\xb4\xd9";
1616

1717
/**
18-
* @param string $file
19-
* @return \Generator
20-
* @throws Exception
21-
*/
22-
public function read(string $file): \Generator
23-
{
24-
$fp = fopen($file, 'r');
25-
26-
if (!$fp) {
27-
throw new Exception("Unable to open block file '$file' for reading.");
28-
}
29-
30-
while (fread($fp, 4) == static::MAGIC) {
31-
yield $this->readBlock($fp);
32-
}
33-
34-
fclose($fp);
35-
}
36-
37-
/**
38-
* @param resource $fp
18+
* @param $fp
19+
* @param int|null $pos
3920
* @return Block
4021
* @throws Exception
4122
*/
42-
public function readBlock($fp): Block
23+
public function readBlockFromFile($fp, int $pos = null): Block
4324
{
4425
if (!is_resource($fp)) {
4526
throw new Exception('Invalid file resource.');
4627
}
4728

29+
if ($pos && fseek($fp, $pos - 4) === false) {
30+
throw new Exception('Unable to seek block file.');
31+
}
32+
4833
$size = fread($fp, 4);
4934

5035
if ($size === false) {
@@ -61,4 +46,45 @@ public function readBlock($fp): Block
6146

6247
return Block::parse(new Reader($data));
6348
}
49+
50+
/**
51+
* @param string $file
52+
* @param int $pos
53+
* @return Block
54+
* @throws Exception
55+
*/
56+
public function readBlock(string $file, int $pos): Block
57+
{
58+
$fp = fopen($file, 'r');
59+
60+
if (!$fp) {
61+
throw new Exception("Unable to open block file '$file'.");
62+
}
63+
64+
$block = $this->readBlockFromFile($fp, $pos);
65+
66+
fclose($fp);
67+
68+
return $block;
69+
}
70+
71+
/**
72+
* @param string $file
73+
* @return \Generator
74+
* @throws Exception
75+
*/
76+
public function read(string $file): \Generator
77+
{
78+
$fp = fopen($file, 'r');
79+
80+
if (!$fp) {
81+
throw new Exception("Unable to open block file '$file'.");
82+
}
83+
84+
while (fread($fp, 4) == static::MAGIC) {
85+
yield $this->readBlockFromFile($fp);
86+
}
87+
88+
fclose($fp);
89+
}
6490
}

src/BlockchainReader.php

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ public function getChainState(): ChainStateReader
7070
return new ChainStateReader($this->chainStateDir);
7171
}
7272

73+
/**
74+
* @param BlockInfo $blockInfo
75+
* @return Block
76+
* @throws Exception
77+
*/
78+
public function getBlockByInfo(BlockInfo $blockInfo): Block
79+
{
80+
$blockFile = $this->blocksDir . DIRECTORY_SEPARATOR . $blockInfo->getFileName();
81+
82+
return (new BlockFileReader())->readBlock($blockFile, $blockInfo->dataPos);
83+
}
84+
85+
/**
86+
* @param string $hash
87+
* @return Block
88+
* @throws Exception
89+
*/
90+
public function getBlockByHash(string $hash): Block
91+
{
92+
return $this->getBlockByInfo($this->getIndex()->getBlockInfoByHash($hash));
93+
}
94+
95+
/**
96+
* @param int $height
97+
* @return Block
98+
* @throws Exception
99+
*/
100+
public function getBlockByHeight(int $height): Block
101+
{
102+
return $this->getBlockByInfo($this->getIndex()->getBlockInfoByHeight($height));
103+
}
104+
73105
/**
74106
* @param int|null $minHeight
75107
* @param int|null $maxHeight
@@ -79,11 +111,9 @@ public function getChainState(): ChainStateReader
79111
public function readBlocks(int $minHeight = null, int $maxHeight = null): \Generator
80112
{
81113
$index = $this->getIndex();
114+
82115
$minHeight = $minHeight ?? $index->getMinHeight();
83116
$maxHeight = $maxHeight ?? $index->getMaxHeight();
84-
$reader = new BlockFileReader();
85-
86-
$handles = [];
87117

88118
for ($i = $minHeight; $i <= $maxHeight; $i++) {
89119
$blockInfo = $index->getBlockInfoByHeight($i);
@@ -92,27 +122,7 @@ public function readBlocks(int $minHeight = null, int $maxHeight = null): \Gener
92122
continue;
93123
}
94124

95-
$file = $blockInfo->getFileName();
96-
97-
if (isset($handles[$file])) {
98-
$fp = $handles[$file];
99-
} else {
100-
$path = $this->blocksDir . DIRECTORY_SEPARATOR . $file;
101-
102-
if (!is_readable($path)) {
103-
throw new Exception("Block file '$file' not found.");
104-
}
105-
106-
$fp = $handles[$file] = fopen($path, 'r');
107-
}
108-
109-
fseek($fp, $blockInfo->dataPos - 4);
110-
111-
yield $i => $reader->readBlock($fp);
112-
}
113-
114-
foreach ($handles as $file => $fp) {
115-
fclose($fp);
125+
yield $i => $this->getBlockByInfo($blockInfo);
116126
}
117127
}
118128

src/Header.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ static public function parse(Reader $stream): self
5151
{
5252
$header = new self;
5353
$header->version = $stream->readUInt32();
54-
$header->prevBlockHash = bin2hex(strrev($stream->read(32)));
55-
$header->merkleRootHash = bin2hex(strrev($stream->read(32)));
54+
$header->prevBlockHash = $stream->read(32);
55+
$header->merkleRootHash = $stream->read(32);
5656
$header->time = $stream->readUInt32();
5757
$header->bits = $stream->readUInt32();
5858
$header->nonce = $stream->readUInt32();
@@ -66,8 +66,8 @@ static public function parse(Reader $stream): self
6666
public function serialize(Writer $stream): self
6767
{
6868
$stream->writeUInt32($this->version);
69-
$stream->write(strrev(hex2bin($this->prevBlockHash)));
70-
$stream->write(strrev(hex2bin($this->merkleRootHash)));
69+
$stream->write($this->prevBlockHash);
70+
$stream->write($this->merkleRootHash);
7171
$stream->writeUInt32($this->time);
7272
$stream->writeUInt32($this->bits);
7373
$stream->writeUInt32($this->nonce);
@@ -81,9 +81,6 @@ public function getHash(): string
8181
{
8282
$stream = new Writer();
8383
$this->serialize($stream);
84-
$hash = Utils::hash($stream->getBuffer(), true);
85-
$hash = strrev($hash);
86-
$hash = bin2hex($hash);
87-
return $hash;
84+
return Utils::hash($stream->getBuffer(), true);
8885
}
8986
}

src/Input.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Input
4545
static public function parse(Reader $stream): self
4646
{
4747
$in = new self;
48-
$in->prevTxHash = bin2hex(strrev($stream->read(32)));
48+
$in->prevTxHash = $stream->read(32);
4949
$in->prevTxOutIndex = $stream->readInt32();
5050
$in->scriptSig = new ScriptSig($stream->readString());
5151
$in->sequenceNo = $stream->readInt32();
@@ -58,7 +58,7 @@ static public function parse(Reader $stream): self
5858
*/
5959
public function serialize(Writer $stream): self
6060
{
61-
$stream->write(strrev(hex2bin($this->prevTxHash)));
61+
$stream->write($this->prevTxHash);
6262
$stream->writeUInt32($this->prevTxOutIndex);
6363
$stream->writeString($this->scriptSig->getData());
6464
$stream->writeInt32($this->sequenceNo);
@@ -70,6 +70,6 @@ public function serialize(Writer $stream): self
7070
*/
7171
public function isCoinbase(): bool
7272
{
73-
return $this->prevTxHash == '0000000000000000000000000000000000000000000000000000000000000000';
73+
return $this->prevTxHash == str_repeat("\x00", 32);
7474
}
7575
}

src/Transaction.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,6 @@ public function getHash(bool $segWit = false): string
137137
{
138138
$stream = new Writer();
139139
$this->serialize($stream, $segWit);
140-
$hash = Utils::hash($stream->getBuffer(), true);
141-
$hash = strrev($hash);
142-
$hash = bin2hex($hash);
143-
return $hash;
140+
return Utils::hash($stream->getBuffer(), true);
144141
}
145142
}

src/Utils.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,22 @@ static public function xor(string $key, string $value): string
7474

7575
return $output;
7676
}
77+
78+
/**
79+
* @param string $hash
80+
* @return string
81+
*/
82+
static public function hashToHex(string $hash): string
83+
{
84+
return bin2hex(strrev($hash));
85+
}
86+
87+
/**
88+
* @param string $hex
89+
* @return string
90+
*/
91+
static public function hexToHash(string $hex): string
92+
{
93+
return strrev(hex2bin($hex));
94+
}
7795
}

0 commit comments

Comments
 (0)