Completed
Pull Request — master (#85)
by thomas
69:21
created

WalletScript   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 192
rs 9.6
c 0
b 0
f 0
wmc 32
lcom 4
cbo 7

11 Methods

Rating   Name   Duplication   Size   Complexity  
A enforceScriptChecks() 0 3 1
B __construct() 0 17 5
A checkP2shScript() 0 15 4
C checkWitnessScript() 0 22 9
A checkScript() 0 19 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
6
use BitWasp\Bitcoin\Address\AddressFactory;
7
use BitWasp\Bitcoin\Address\AddressInterface;
8
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
9
use BitWasp\Bitcoin\Script\P2shScript;
10
use BitWasp\Bitcoin\Script\ScriptInterface;
11
use BitWasp\Bitcoin\Script\ScriptType;
12
use BitWasp\Bitcoin\Script\WitnessProgram;
13
use BitWasp\Bitcoin\Script\WitnessScript;
14
use BitWasp\Buffertools\BufferInterface;
15
use Blocktrail\SDK\Bitcoin\BIP32Path;
16
17
class WalletScript
18
{
19
    const DEFAULT_SHOULD_CHECK = false;
20
21
    /**
22
     * @var null|bool
23
     */
24
    protected static $checkScripts;
25
26
    /**
27
     * @var \BitWasp\Bitcoin\Address\AddressInterface|null
28
     */
29
    private $address;
30
31
    /**
32
     * @var ScriptInterface
33
     */
34
    private $spk;
35
36
    /**
37
     * @var P2shScript|null
38
     */
39
    private $redeemScript;
40
41
    /**
42
     * @var WitnessScript|null
43
     */
44
    private $witnessScript;
45
46
    /**
47
     * @var BIP32Path
48
     */
49
    private $path;
50
51
    /**
52
     * Disables script checking (procedure includes some hashing)
53
     * @param bool $setting
54
     */
55
    public static function enforceScriptChecks($setting) {
56
        static::$checkScripts = (bool) $setting;
57
    }
58
59
    /**
60
     * WalletScript constructor.
61
     * @param BIP32Path $path
62
     * @param ScriptInterface $spk
63
     * @param P2shScript|null $redeemScript
64
     * @param WitnessScript|null $witnessScript
65
     */
66
    public function __construct(BIP32Path $path, ScriptInterface $spk, P2shScript $redeemScript = null, WitnessScript $witnessScript = null)
67
    {
68
        if (static::$checkScripts || null === static::$checkScripts && self::DEFAULT_SHOULD_CHECK) {
69
            $this->checkScript($path[2], $spk, $redeemScript, $witnessScript);
70
        }
71
72
        $this->path = $path;
73
        $this->spk = $spk;
74
        $this->redeemScript = $redeemScript;
75
        $this->witnessScript = $witnessScript;
76
77
        try {
78
            $this->address = AddressFactory::fromOutputScript($spk);
79
        } catch (\Exception $e) {
80
            // doesn't matter
81
        }
82
    }
83
84
    /**
85
     * @param OutputClassifier $classifier
86
     * @param ScriptInterface $script
87
     * @param P2shScript|null $redeemScript
88
     */
89
    protected function checkP2shScript(OutputClassifier $classifier, ScriptInterface $script, P2shScript $redeemScript = null)
90
    {
91
        $scriptHash = null;
92
        if (!($classifier->classify($script, $scriptHash) === ScriptType::P2SH)) {
93
            throw new \RuntimeException("scriptPubKey should be script-hash");
94
        }
95
        /** @var BufferInterface $scriptHash */
96
        if (null === $redeemScript) {
97
            throw new \RuntimeException("Missing redeemScript");
98
        }
99
100
        if (!$redeemScript->getScriptHash()->equals($scriptHash)) {
101
            throw new \RuntimeException("Invalid redeemScript for scriptPubKey");
102
        }
103
    }
104
105
    /**
106
     * @param ScriptInterface $script
107
     * @param WitnessScript|null $witnessScript
108
     */
109
    protected function checkWitnessScript(ScriptInterface $script, WitnessScript $witnessScript = null)
110
    {
111
        if (!$script->isWitness($wp)) {
112
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
113
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
114
        }
115
116
        /** @var WitnessProgram $wp */
117
        if ($wp->getProgram()->getSize() !== 32) {
118
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "Script";
119
            throw new \RuntimeException("$errSuffix should be v0_scripthash");
120
        }
121
122
        if (null === $witnessScript) {
123
            $errSuffix = $script instanceof P2shScript ? "P2SH script" : "script";
124
            throw new \RuntimeException("Missing witnessScript for $errSuffix");
125
        }
126
127
        if (null === $witnessScript || !$wp->getProgram()->equals($witnessScript->getScriptHash())) {
128
            throw new \RuntimeException("Invalid witnessScript for p2sh v0_scripthash");
129
        }
130
    }
131
132
    /**
133
     * @param int $scriptId
134
     * @param ScriptInterface $script
135
     * @param P2shScript|null $redeemScript
136
     * @param WitnessScript|null $witnessScript
137
     */
138
    protected function checkScript($scriptId, ScriptInterface $script, P2shScript $redeemScript = null, WitnessScript $witnessScript = null)
139
    {
140
        $classifier = new OutputClassifier();
141
        switch ($scriptId) {
142
            case 2:
143
                $this->checkP2shScript($classifier, $script, $redeemScript);
144
                $this->checkWitnessScript($redeemScript, $witnessScript);
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 138 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...
145
                if (!$classifier->isMultisig($witnessScript)) {
0 ignored issues
show
Bug introduced by
It seems like $witnessScript defined by parameter $witnessScript on line 138 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...
146
                    throw new \RuntimeException("Expected multisig as witnessScript");
147
                }
148
                break;
149
            default:
150
                $this->checkP2shScript($classifier, $script, $redeemScript);
151
                if (!$classifier->isMultisig($redeemScript)) {
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 138 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...
152
                    throw new \RuntimeException("Expected multisig as redeemScript");
153
                }
154
                break;
155
        }
156
    }
157
158
    /**
159
     * @return ScriptInterface
160
     */
161
    public function getScriptPubKey() {
162
        return $this->spk;
163
    }
164
165
    /**
166
     * @return bool
167
     */
168
    public function isP2SH() {
169
        return $this->redeemScript instanceof P2shScript;
170
    }
171
172
    /**
173
     * @return bool
174
     */
175
    public function isP2WSH() {
176
        return $this->witnessScript instanceof WitnessScript;
177
    }
178
179
    /**
180
     * @return WitnessScript|null
181
     */
182
    public function getWitnessScript() {
183
        if (null === $this->witnessScript) {
184
            throw new \RuntimeException("WitnessScript not set");
185
        }
186
        return $this->witnessScript;
187
    }
188
189
    /**
190
     * @return P2shScript|null
191
     */
192
    public function getRedeemScript() {
193
        if (null === $this->redeemScript) {
194
            throw new \RuntimeException("RedeemScript not set");
195
        }
196
        return $this->redeemScript;
197
    }
198
199
    /**
200
     * @return AddressInterface|null
201
     */
202
    public function getAddress() {
203
        if (null === $this->address) {
204
            throw new \RuntimeException("This script has no address");
205
        }
206
        return $this->address;
207
    }
208
}
209