Completed
Branch master (7ca1f6)
by
unknown
02:09
created

WalletScript::__construct()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.2

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 4
nop 4
dl 0
loc 16
ccs 8
cts 10
cp 0.8
crap 5.2
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace Blocktrail\SDK;
4
5
use BitWasp\Bitcoin\Address\AddressFactory;
6
use BitWasp\Bitcoin\Address\AddressInterface;
7
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
8
use BitWasp\Bitcoin\Script\P2shScript;
9
use BitWasp\Bitcoin\Script\ScriptInterface;
10
use BitWasp\Bitcoin\Script\ScriptType;
11
use BitWasp\Bitcoin\Script\WitnessProgram;
12
use BitWasp\Bitcoin\Script\WitnessScript;
13
use BitWasp\Buffertools\BufferInterface;
14
use Blocktrail\SDK\Bitcoin\BIP32Path;
15
16
class WalletScript
17
{
18
    const DEFAULT_SHOULD_CHECK = false;
19
20
    /**
21
     * @var null|bool
22
     */
23
    protected static $checkScripts;
24
25
    /**
26
     * @var \BitWasp\Bitcoin\Address\AddressInterface|null
27
     */
28
    private $address;
29
30
    /**
31
     * @var ScriptInterface
32
     */
33
    private $spk;
34
35
    /**
36
     * @var P2shScript|null
37
     */
38
    private $redeemScript;
39
40
    /**
41
     * @var WitnessScript|null
42
     */
43
    private $witnessScript;
44
45
    /**
46
     * @var BIP32Path
47
     */
48
    private $path;
49
50
    /**
51
     * Disables script checking (procedure includes some hashing)
52
     * @param bool $setting
53
     */
54
    public static function enforceScriptChecks($setting) {
55
        static::$checkScripts = (bool) $setting;
56
    }
57
58
    /**
59
     * WalletScript constructor.
60
     * @param BIP32Path $path
61
     * @param ScriptInterface $spk
62
     * @param P2shScript|null $redeemScript
63
     * @param WitnessScript|null $witnessScript
64
     */
65 14
    public function __construct(BIP32Path $path, ScriptInterface $spk, P2shScript $redeemScript = null, WitnessScript $witnessScript = null) {
66 14
        if (static::$checkScripts || null === static::$checkScripts && self::DEFAULT_SHOULD_CHECK) {
67
            $this->checkScript($path[2], $spk, $redeemScript, $witnessScript);
68
        }
69
70 14
        $this->path = $path;
71 14
        $this->spk = $spk;
72 14
        $this->redeemScript = $redeemScript;
73 14
        $this->witnessScript = $witnessScript;
74
75
        try {
76 14
            $this->address = AddressFactory::fromOutputScript($spk);
77
        } catch (\Exception $e) {
78
            // doesn't matter
79
        }
80 14
    }
81
82
    /**
83
     * @param OutputClassifier $classifier
84
     * @param ScriptInterface $script
85
     * @param P2shScript|null $redeemScript
86
     */
87
    protected function checkP2shScript(OutputClassifier $classifier, ScriptInterface $script, P2shScript $redeemScript = null) {
88
        $scriptHash = null;
89
        if (!($classifier->classify($script, $scriptHash) === ScriptType::P2SH)) {
90
            throw new \RuntimeException("scriptPubKey should be script-hash");
91
        }
92
        /** @var BufferInterface $scriptHash */
93
        if (null === $redeemScript) {
94
            throw new \RuntimeException("Missing redeemScript");
95
        }
96
97
        if (!$redeemScript->getScriptHash()->equals($scriptHash)) {
98
            throw new \RuntimeException("Invalid redeemScript for scriptPubKey");
99
        }
100
    }
101
102
    /**
103
     * @param ScriptInterface $script
104
     * @param WitnessScript|null $witnessScript
105
     */
106
    protected function checkWitnessScript(ScriptInterface $script, WitnessScript $witnessScript = null) {
107
        if (!$script->isWitness($wp)) {
108
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
109
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
110
        }
111
112
        /** @var WitnessProgram $wp */
113
        if ($wp->getProgram()->getSize() !== 32) {
114
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
115
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
116
        }
117
118
        if (null === $witnessScript) {
119
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "script";
120
            throw new \RuntimeException("Missing witnessScript for $errSuffix");
121
        }
122
123
        if (null === $witnessScript || !$wp->getProgram()->equals($witnessScript->getScriptHash())) {
124
            throw new \RuntimeException("Invalid witnessScript for p2sh v0_scripthash");
125
        }
126
    }
127
128
    /**
129
     * @param int $scriptId
130
     * @param ScriptInterface $script
131
     * @param P2shScript|null $redeemScript
132
     * @param WitnessScript|null $witnessScript
133
     */
134
    protected function checkScript($scriptId, ScriptInterface $script, P2shScript $redeemScript = null, WitnessScript $witnessScript = null) {
135
        $classifier = new OutputClassifier();
136
        switch ($scriptId) {
137
            case 2:
138
                $this->checkP2shScript($classifier, $script, $redeemScript);
139
                $this->checkWitnessScript($redeemScript, $witnessScript);
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 134 can be null; however, Blocktrail\SDK\WalletScript::checkWitnessScript() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
140
                if (!$classifier->isMultisig($witnessScript)) {
0 ignored issues
show
Bug introduced by
It seems like $witnessScript defined by parameter $witnessScript on line 134 can be null; however, BitWasp\Bitcoin\Script\C...lassifier::isMultisig() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
141
                    throw new \RuntimeException("Expected multisig as witnessScript");
142
                }
143
                break;
144
            default:
145
                $this->checkP2shScript($classifier, $script, $redeemScript);
146
                if (!$classifier->isMultisig($redeemScript)) {
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 134 can be null; however, BitWasp\Bitcoin\Script\C...lassifier::isMultisig() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
147
                    throw new \RuntimeException("Expected multisig as redeemScript");
148
                }
149
                break;
150
        }
151
    }
152
153
    /**
154
     * @return ScriptInterface
155
     */
156 1
    public function getScriptPubKey() {
157 1
        return $this->spk;
158
    }
159
160
    /**
161
     * @return bool
162
     */
163 14
    public function isP2SH() {
164 14
        return $this->redeemScript instanceof P2shScript;
165
    }
166
167
    /**
168
     * @return bool
169
     */
170 14
    public function isP2WSH() {
171 14
        return $this->witnessScript instanceof WitnessScript;
172
    }
173
174
    /**
175
     * @return WitnessScript|null
176
     */
177 3
    public function getWitnessScript() {
178 3
        if (null === $this->witnessScript) {
179
            throw new \RuntimeException("WitnessScript not set");
180
        }
181 3
        return $this->witnessScript;
182
    }
183
184
    /**
185
     * @return P2shScript|null
186
     */
187 14
    public function getRedeemScript() {
188 14
        if (null === $this->redeemScript) {
189
            throw new \RuntimeException("RedeemScript not set");
190
        }
191 14
        return $this->redeemScript;
192
    }
193
194
    /**
195
     * @return AddressInterface|null
196
     */
197 14
    public function getAddress() {
198 14
        if (null === $this->address) {
199
            throw new \RuntimeException("This script has no address");
200
        }
201 14
        return $this->address;
202
    }
203
}
204