Completed
Push — master ( 319c7a...cdee94 )
by thomas
79:06 queued 75:43
created

OutputClassifier::isPayToPublicKey()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.049

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 0
loc 16
ccs 9
cts 10
cp 0.9
rs 8.2222
cc 7
eloc 9
nc 4
nop 0
crap 7.049
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Classifier;
4
5
use BitWasp\Bitcoin\Script\Parser\Operation;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;
7
use BitWasp\Bitcoin\Script\Opcodes;
8
use BitWasp\Bitcoin\Script\ScriptInterface;
9
10
class OutputClassifier implements ScriptClassifierInterface
11
{
12
    /**
13
     * @var \BitWasp\Bitcoin\Script\Parser\Operation[]
14
     */
15
    private $decoded;
16
17
    /**
18
     * @param ScriptInterface $script
19
     */
20 645
    public function __construct(ScriptInterface $script)
21
    {
22 645
        $this->decoded = $script->getScriptParser()->decode();
23 645
    }
24
25
    /**
26
     * @return bool
27
     */
28 201
    public function isPayToPublicKey()
29
    {
30 201
        if (count($this->decoded) < 1 || !$this->decoded[0]->isPush()) {
31 159
            return false;
32
        }
33
34 60
        $size = $this->decoded[0]->getDataSize();
35 60
        if ($size === 33 || $size === 65) {
36 42
            $op = $this->decoded[1];
37 42
            if (!$op->isPush() && $op->getOp() === Opcodes::OP_CHECKSIG) {
38 42
                return true;
39
            }
40
        }
41
42 18
        return false;
43
    }
44
45
    /**
46
     * @return bool
47
     */
48 159
    public function isPayToPublicKeyHash()
49
    {
50 159
        if (count($this->decoded) !== 5) {
51 60
            return false;
52
        }
53
54 111
        $dup = $this->decoded[0];
55 111
        $hash = $this->decoded[1];
56 111
        $buf = $this->decoded[2];
57 111
        $eq = $this->decoded[3];
58 111
        $checksig = $this->decoded[4];
59
60 111
        foreach ([$dup, $hash, $eq, $checksig] as $op) {
61
            /** @var Operation $op */
62 111
            if ($op->isPush()) {
63 24
                return false;
64
            }
65 111
        }
66
67 87
        return $dup->getOp() === Opcodes::OP_DUP
68 87
        && $hash->getOp() === Opcodes::OP_HASH160
69 87
        && $buf->isPush() && $buf->getDataSize() === 20
70 87
        && $eq->getOp() === Opcodes::OP_EQUALVERIFY
71 87
        && $checksig->getOp() === Opcodes::OP_CHECKSIG;
72
    }
73
74
    /**
75
     * @return bool
76
     */
77 291
    public function isPayToScriptHash()
78
    {
79 291
        if (count($this->decoded) !== 3) {
80 237
            return false;
81
        }
82
83 108
        $hash = $this->decoded[0];
84 108
        if ($hash->isPush() || !$hash->getOp() === Opcodes::OP_HASH160) {
85 18
            return false;
86
        }
87
88 90
        $buffer = $this->decoded[1];
89 90
        if (!$buffer->isPush() || $buffer->getDataSize() !== 20) {
90 6
            return false;
91
        }
92
93 84
        $eq = $this->decoded[2];
94 84
        return !$eq->isPush() && $eq->getOp() === Opcodes::OP_EQUAL;
95
    }
96
97
    /**
98
     * @return bool
99
     */
100 171
    public function isMultisig()
101
    {
102 171
        $count = count($this->decoded);
103 171
        if ($count <= 3) {
104 60
            return false;
105
        }
106
107 111
        $mOp = $this->decoded[0];
108 111
        $nOp = $this->decoded[$count - 2];
109 111
        $checksig = $this->decoded[$count - 1];
110 111
        if ($mOp->isPush() || $nOp->isPush() || $checksig->isPush()) {
111
            return false;
112
        }
113
114
        /** @var Operation[] $vKeys */
115 111
        $vKeys = array_slice($this->decoded, 1, -2);
116 111
        foreach ($vKeys as $key) {
117 111
            if (!$key->isPush() || !PublicKey::isCompressedOrUncompressed($key->getData())) {
118 57
                return false;
119
            }
120 54
        }
121
122 54
        return $mOp->getOp() >= Opcodes::OP_0
123 54
            && $nOp->getOp() <= Opcodes::OP_16
124 54
            && $checksig->getOp() === Opcodes::OP_CHECKMULTISIG;
125
    }
126
127
    /**
128
     * @return string
129
     */
130 102
    public function classify()
131
    {
132 102
        if ($this->isPayToPublicKey()) {
133 6
            return self::PAYTOPUBKEY;
134 96
        } elseif ($this->isPayToPublicKeyHash()) {
135 30
            return self::PAYTOPUBKEYHASH;
136 78
        } elseif ($this->isPayToScriptHash()) {
137 18
            return self::PAYTOSCRIPTHASH;
138 66
        } elseif ($this->isMultisig()) {
139 48
            return self::MULTISIG;
140
        }
141
142 18
        return self::UNKNOWN;
143
    }
144
}
145