Skip to content

Commit d4604c0

Browse files
committed
fast output type checking
1 parent e9840c9 commit d4604c0

File tree

4 files changed

+60
-40
lines changed

4 files changed

+60
-40
lines changed

src/AddressSerializer.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace AndKom\Bitcoin\Blockchain;
66

7-
use AndKom\Bitcoin\Blockchain\Exception\AddressException;
87
use AndKom\Bitcoin\Blockchain\Network\Bitcoin;
98
use function BitWasp\Bech32\encodeSegwit;
109
use StephenHill\Base58;

src/Script.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class Script
1717
*/
1818
public $data;
1919

20+
/**
21+
* @var
22+
*/
23+
public $size;
24+
2025
/**
2126
* @var array
2227
*/
@@ -54,6 +59,7 @@ public function getData(): string
5459
public function setData(string $data): self
5560
{
5661
$this->data = $data;
62+
$this->size = strlen($data);
5763
$this->operations = null;
5864
return $this;
5965
}

src/ScriptPubKey.php

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,89 +18,104 @@ class ScriptPubKey extends Script
1818
*/
1919
public function isReturn(): bool
2020
{
21-
$operations = $this->parse();
22-
23-
return count($operations) >= 1 &&
24-
$operations[0]->code == Opcodes::OP_RETURN;
21+
return $this->size >= 1 && ord($this->data[0]) == Opcodes::OP_RETURN;
2522
}
2623

2724
/**
2825
* @return bool
2926
*/
3027
public function isPayToPubKey(): bool
3128
{
32-
$operations = $this->parse();
29+
// compressed pubkey
30+
if ($this->size == 35 &&
31+
ord($this->data[0]) == 33 &&
32+
ord($this->data[-1]) == Opcodes::OP_CHECKSIG &&
33+
($this->data[1] == "\x02" || $this->data[1] == "\x03")) {
34+
return true;
35+
}
36+
37+
// uncompressed pubkey
38+
if ($this->size == 67 &&
39+
ord($this->data[0]) == 65 &&
40+
ord($this->data[-1]) == Opcodes::OP_CHECKSIG &&
41+
$this->data[1] == "\x04") {
42+
return true;
43+
}
3344

34-
return count($operations) == 2 &&
35-
($operations[0]->size == 33 || $operations[0]->size == 65) &&
36-
$operations[1]->code == Opcodes::OP_CHECKSIG;
45+
return false;
3746
}
3847

3948
/**
4049
* @return bool
4150
*/
4251
public function isPayToPubKeyHash(): bool
4352
{
44-
$operations = $this->parse();
45-
46-
return count($operations) == 5 &&
47-
$operations[0]->code == Opcodes::OP_DUP &&
48-
$operations[1]->code == Opcodes::OP_HASH160 &&
49-
$operations[2]->size == 20 &&
50-
$operations[3]->code == Opcodes::OP_EQUALVERIFY &&
51-
$operations[4]->code == Opcodes::OP_CHECKSIG;
53+
return $this->size == 25 &&
54+
ord($this->data[0]) == Opcodes::OP_DUP &&
55+
ord($this->data[1]) == Opcodes::OP_HASH160 &&
56+
ord($this->data[2]) == 20 &&
57+
ord($this->data[-2]) == Opcodes::OP_EQUALVERIFY &&
58+
ord($this->data[-1]) == Opcodes::OP_CHECKSIG;
5259
}
5360

5461
/**
5562
* @return bool
5663
*/
5764
public function isPayToScriptHash(): bool
5865
{
59-
$operations = $this->parse();
60-
61-
return count($operations) == 3 &&
62-
$operations[0]->code == Opcodes::OP_HASH160 &&
63-
$operations[1]->size == 20 &&
64-
$operations[2]->code == Opcodes::OP_EQUAL;
66+
return $this->size == 23 &&
67+
ord($this->data[0]) == Opcodes::OP_HASH160 &&
68+
ord($this->data[1]) == 20 &&
69+
ord($this->data[-1]) == Opcodes::OP_EQUAL;
6570
}
6671

6772
/**
6873
* @return bool
6974
*/
7075
public function isMultisig(): bool
7176
{
72-
$operations = $this->parse();
77+
$keys = ord($this->data[0]);
78+
$sigs = ord($this->data[-2]);
79+
80+
if (!($this->size >= 24 &&
81+
$keys >= Opcodes::OP_1 && $keys <= Opcodes::OP_16 &&
82+
$sigs && $sigs <= Opcodes::OP_16 &&
83+
$keys >= $sigs &&
84+
ord($this->data[-1]) == Opcodes::OP_CHECKMULTISIG)) {
85+
return false;
86+
}
87+
88+
for ($i = 1, $k = 0; $i < $this->size - 2; $i += 21, $k++) {
89+
if (ord($this->data[$i]) != 20) {
90+
return false;
91+
}
92+
}
7393

74-
return ($count = count($operations)) >= 4 &&
75-
$operations[0]->code >= Opcodes::OP_1 &&
76-
$operations[$count - 2]->code >= Opcodes::OP_1 &&
77-
$operations[$count - 1]->code == Opcodes::OP_CHECKMULTISIG;
94+
return $keys - Opcodes::OP_1 + 1 == $k;
7895
}
7996

8097
/**
8198
* @return bool
8299
*/
83100
public function isPayToWitnessPubKeyHash(): bool
84101
{
85-
$operations = $this->parse();
102+
$version = ord($this->data[0]);
86103

87-
return count($operations) == 2 &&
88-
$operations[0]->code >= Opcodes::OP_0 &&
89-
$operations[0]->code <= Opcodes::OP_16 &&
90-
$operations[1]->size == 20;
104+
return $this->size == 22 &&
105+
$version >= Opcodes::OP_0 && $version <= Opcodes::OP_16 &&
106+
ord($this->data[1]) == 20;
91107
}
92108

93109
/**
94110
* @return bool
95111
*/
96112
public function isPayToWitnessScriptHash(): bool
97113
{
98-
$operations = $this->parse();
114+
$version = ord($this->data[0]);
99115

100-
return count($operations) == 2 &&
101-
$operations[0]->code >= Opcodes::OP_0 &&
102-
$operations[0]->code <= Opcodes::OP_16 &&
103-
$operations[1]->size == 32;
116+
return $this->size == 34 &&
117+
$version >= Opcodes::OP_0 && $version <= Opcodes::OP_16 &&
118+
ord($this->data[1]) == 32;
104119
}
105120

106121
/**

tests/ScriptPubKeyTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ public function testParseP2SH()
7878
// [numsigs] [...pubkeys...] [numpubkeys] OP_CHECKMULTISIG
7979
public function testParseMultisig()
8080
{
81-
$hex = '52'; // OP_2
81+
$hex = '53'; // OP_3
8282
$hex .= '14'; // OP_PUSHDATA
8383
$hex .= '91b24bf9f5288532960ac687abb035127b1d28a5'; // pubkey hash
8484
$hex .= '14'; // OP_PUSHDATA
8585
$hex .= 'd6c8e828c1eca1bba065e1b83e1dc2a36e387a42'; // pubkey hash
8686
$hex .= '14'; // OP_PUSHDATA
8787
$hex .= 'ec7eced2c57ed1292bc4eb9bfd13c9f7603bc338'; // pubkey hash
88-
$hex .= '53'; // OP_3
88+
$hex .= '52'; // OP_2
8989
$hex .= 'ae'; // OP_CHECKMULTISIG
9090

9191
$script = new ScriptPubKey(hex2bin($hex));

0 commit comments

Comments
 (0)