Completed
Branch master (6cbd51)
by
unknown
02:55
created

WalletScript   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 8

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 195
ccs 0
cts 98
cp 0
rs 9.3999
c 0
b 0
f 0
wmc 33
lcom 4
cbo 8

11 Methods

Rating   Name   Duplication   Size   Complexity  
A enforceScriptChecks() 0 3 1
B __construct() 0 23 6
A checkP2shScript() 0 14 4
B checkWitnessScript() 0 21 9
A checkScript() 0 18 4
A getScriptPubKey() 0 3 1
A isP2SH() 0 3 1
A isP2WSH() 0 3 1
A getWitnessScript() 0 6 2
A getRedeemScript() 0 6 2
A getAddress() 0 6 2
1
<?php
2
3
namespace Blocktrail\SDK;
4
5
use BitWasp\Bitcoin\Address\AddressInterface;
6
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
7
use BitWasp\Bitcoin\Script\P2shScript;
8
use BitWasp\Bitcoin\Script\ScriptInterface;
9
use BitWasp\Bitcoin\Script\ScriptType;
10
use BitWasp\Bitcoin\Script\WitnessProgram;
11
use BitWasp\Bitcoin\Script\WitnessScript;
12
use BitWasp\Buffertools\BufferInterface;
13
use Blocktrail\SDK\Bitcoin\BIP32Path;
14
use Blocktrail\SDK\Exceptions\BlocktrailSDKException;
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
    public function __construct(
66
        BIP32Path $path,
67
        ScriptInterface $spk,
68
        P2shScript $redeemScript = null,
69
        WitnessScript $witnessScript = null,
70
        AddressInterface $address = null
71
    ) {
72
        if (static::$checkScripts || null === static::$checkScripts && self::DEFAULT_SHOULD_CHECK) {
73
            $this->checkScript($path[2], $spk, $redeemScript, $witnessScript);
74
        }
75
76
        $this->path = $path;
77
        $this->spk = $spk;
78
        $this->redeemScript = $redeemScript;
79
        $this->witnessScript = $witnessScript;
80
81
        if ($address) {
82
            if (!$address->getScriptPubKey()->equals($this->getScriptPubKey())) {
83
                throw new BlocktrailSDKException("Mismatch between scriptPubKey and address");
84
            }
85
            $this->address = $address;
86
        }
87
    }
88
89
    /**
90
     * @param OutputClassifier $classifier
91
     * @param ScriptInterface $script
92
     * @param P2shScript|null $redeemScript
93
     */
94
    protected function checkP2shScript(OutputClassifier $classifier, ScriptInterface $script, P2shScript $redeemScript = null) {
95
        $scriptHash = null;
96
        if (!($classifier->classify($script, $scriptHash) === ScriptType::P2SH)) {
97
            throw new \RuntimeException("scriptPubKey should be script-hash");
98
        }
99
        /** @var BufferInterface $scriptHash */
100
        if (null === $redeemScript) {
101
            throw new \RuntimeException("Missing redeemScript");
102
        }
103
104
        if (!$redeemScript->getScriptHash()->equals($scriptHash)) {
105
            throw new \RuntimeException("Invalid redeemScript for scriptPubKey");
106
        }
107
    }
108
109
    /**
110
     * @param ScriptInterface $script
111
     * @param WitnessScript|null $witnessScript
112
     */
113
    protected function checkWitnessScript(ScriptInterface $script, WitnessScript $witnessScript = null) {
114
        if (!$script->isWitness($wp)) {
115
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
116
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
117
        }
118
119
        /** @var WitnessProgram $wp */
120
        if ($wp->getProgram()->getSize() !== 32) {
121
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
122
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
123
        }
124
125
        if (null === $witnessScript) {
126
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "script";
127
            throw new \RuntimeException("Missing witnessScript for $errSuffix");
128
        }
129
130
        if (null === $witnessScript || !$wp->getProgram()->equals($witnessScript->getScriptHash())) {
131
            throw new \RuntimeException("Invalid witnessScript for p2sh v0_scripthash");
132
        }
133
    }
134
135
    /**
136
     * @param int $scriptId
137
     * @param ScriptInterface $script
138
     * @param P2shScript|null $redeemScript
139
     * @param WitnessScript|null $witnessScript
140
     */
141
    protected function checkScript($scriptId, ScriptInterface $script, P2shScript $redeemScript = null, WitnessScript $witnessScript = null) {
142
        $classifier = new OutputClassifier();
143
        switch ($scriptId) {
144
            case 2:
145
                $this->checkP2shScript($classifier, $script, $redeemScript);
146
                $this->checkWitnessScript($redeemScript, $witnessScript);
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 141 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...
147
                if (!$classifier->isMultisig($witnessScript)) {
0 ignored issues
show
Bug introduced by
It seems like $witnessScript defined by parameter $witnessScript on line 141 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...
148
                    throw new \RuntimeException("Expected multisig as witnessScript");
149
                }
150
                break;
151
            default:
152
                $this->checkP2shScript($classifier, $script, $redeemScript);
153
                if (!$classifier->isMultisig($redeemScript)) {
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 141 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...
154
                    throw new \RuntimeException("Expected multisig as redeemScript");
155
                }
156
                break;
157
        }
158
    }
159
160
    /**
161
     * @return ScriptInterface
162
     */
163
    public function getScriptPubKey() {
164
        return $this->spk;
165
    }
166
167
    /**
168
     * @return bool
169
     */
170
    public function isP2SH() {
171
        return $this->redeemScript instanceof P2shScript;
172
    }
173
174
    /**
175
     * @return bool
176
     */
177
    public function isP2WSH() {
178
        return $this->witnessScript instanceof WitnessScript;
179
    }
180
181
    /**
182
     * @return WitnessScript|null
183
     */
184
    public function getWitnessScript() {
185
        if (null === $this->witnessScript) {
186
            throw new \RuntimeException("WitnessScript not set");
187
        }
188
        return $this->witnessScript;
189
    }
190
191
    /**
192
     * @return P2shScript|null
193
     */
194
    public function getRedeemScript() {
195
        if (null === $this->redeemScript) {
196
            throw new \RuntimeException("RedeemScript not set");
197
        }
198
        return $this->redeemScript;
199
    }
200
201
    /**
202
     * @return AddressInterface|null
203
     */
204
    public function getAddress() {
205
        if (null === $this->address) {
206
            throw new \RuntimeException("This script has no address");
207
        }
208
        return $this->address;
209
    }
210
}
211