Completed
Pull Request — master (#89)
by thomas
20:32
created

WalletV3::unlock()   C

Complexity

Conditions 14
Paths 92

Size

Total Lines 55
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 19.9814

Importance

Changes 0
Metric Value
cc 14
eloc 33
nc 92
nop 2
dl 0
loc 55
ccs 22
cts 32
cp 0.6875
crap 19.9814
rs 6.7491
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Blocktrail\SDK;
4
5
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory;
6
use BitWasp\Buffertools\Buffer;
7
use BitWasp\Buffertools\BufferInterface;
8
use Blocktrail\SDK\Bitcoin\BIP32Key;
9
use Blocktrail\SDK\Exceptions\BlocktrailSDKException;
10
use Blocktrail\SDK\Exceptions\WalletDecryptException;
11
use Blocktrail\SDK\V3Crypt\Encryption;
12
use Blocktrail\SDK\V3Crypt\EncryptionMnemonic;
13
14
class WalletV3 extends Wallet
15
{
16
17
    /**
18
     * @var BufferInterface
19
     */
20
    protected $encryptedPrimarySeed;
21
22
    /**
23
     * @var BufferInterface
24
     */
25
    protected $encryptedSecret;
26
27
    /**
28
     * @var BufferInterface
29
     */
30
    protected $secret = null;
31
32
    /**
33
     * @var BufferInterface
34
     */
35
    protected $primarySeed = null;
36
37
    /**
38
     * @param BlocktrailSDKInterface $sdk                   SDK instance used to do requests
39
     * @param string                 $identifier            identifier of the wallet
40
     * @param BufferInterface        $encryptedPrimarySeed
41
     * @param BufferInterface        $encryptedSecret
42
     * @param BIP32Key[]             $primaryPublicKeys
43
     * @param BIP32Key               $backupPublicKey
44
     * @param BIP32Key[]             $blocktrailPublicKeys
45
     * @param int                    $keyIndex
46
     * @param string                 $checksum
47
     */
48 4
    public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum) {
49 4
        if ($encryptedPrimarySeed !== null && !($encryptedPrimarySeed instanceof Buffer)) {
50
            throw new \InvalidArgumentException('Encrypted Primary Seed must be a Buffer or null');
51
        }
52 4
        if ($encryptedSecret !== null && !($encryptedSecret instanceof Buffer)) {
53
            throw new \InvalidArgumentException('Encrypted Secret must be a Buffer or null');
54
        }
55 4
        $this->encryptedPrimarySeed = $encryptedPrimarySeed;
56 4
        $this->encryptedSecret = $encryptedSecret;
57
58 4
        parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum);
59 4
    }
60
61
    /**
62
     * unlock wallet so it can be used for payments
63
     *
64
     * @param          $options ['primary_private_key' => key] OR ['passphrase' => pass]
65
     * @param callable $fn
66
     * @return bool
67
     * @throws \Exception
68
     */
69 4
    public function unlock($options, callable $fn = null) {
70
        // explode the wallet data
71 4
        $password = isset($options['passphrase']) ? $options['passphrase'] : (isset($options['password']) ? $options['password'] : null);
72
73 4
        $encryptedPrimarySeed = $this->encryptedPrimarySeed;
74 4
        $encryptedSecret = $this->encryptedSecret;
75
76 4
        if (isset($options['primary_seed'])) {
77 4
            if (!$options['primary_seed'] instanceof BufferInterface) {
78
                throw new \RuntimeException('Primary Seed must be a BufferInterface');
79
            }
80 4
            $this->primarySeed = $options['primary_seed'];
81
        } else {
82 4
            if (!$password) {
83
                throw new \InvalidArgumentException("Can't init wallet with Primary Seed without a passphrase");
84 4
            } elseif (!$encryptedSecret) {
85
                throw new \InvalidArgumentException("Can't init wallet with Primary Seed without a encrypted secret");
86 4
            } elseif (!$encryptedPrimarySeed) {
87
                throw new \InvalidArgumentException("Can't init wallet without an Encrypted Primary Seed");
88
            }
89
90 4
            if (!$password instanceof Buffer) {
91 4
                $password = new Buffer($password);
92
            }
93 4
            if (!($this->secret = Encryption::decrypt($encryptedSecret, $password))) {
94
                throw new WalletDecryptException("Failed to decrypt secret with password");
95
            }
96
97 4
            if (!($this->primarySeed = Encryption::decrypt($encryptedPrimarySeed, $this->secret))) {
98
                throw new WalletDecryptException("Failed to decrypt primary seed with secret");
99
            }
100
        }
101 4
        $network = $this->networkParams->getNetwork();
0 ignored issues
show
Bug introduced by
The method getNetwork() does not seem to exist on object<BitWasp\Bitcoin\Network\NetworkInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
102 4
        $this->primaryPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($this->primarySeed), "m");
103
104
        // create checksum (address) of the primary privatekey to compare to the stored checksum
105 4
        $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress($network);
106 4
        if ($checksum != $this->checksum) {
107
            throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password");
108
        }
109
110 4
        $this->locked = false;
111
112
        // if the response suggests we should upgrade to a different blocktrail cosigning key then we should
113 4
        if (isset($data['upgrade_key_index'])) {
0 ignored issues
show
Bug introduced by
The variable $data seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
114
            $this->upgradeKeyIndex($data['upgrade_key_index']);
115
        }
116
117 4
        if ($fn) {
118
            $fn($this);
119
            $this->lock();
120
        }
121
122 4
        return true;
123
    }
124
125
    /**
126
     * lock the wallet (unsets primary private key)
127
     *
128
     * @return void
129
     */
130 2
    public function lock() {
131 2
        $this->primaryPrivateKey = null;
132 2
        $this->secret = null;
133 2
        $this->primarySeed = null;
134 2
        $this->locked = true;
135 2
    }
136
137
    /**
138
     * change password that is used to store data encrypted on server
139
     *
140
     * @param string $newPassword
141
     * @return array backupInfo
142
     * @throws BlocktrailSDKException
143
     */
144 2
    public function passwordChange($newPassword) {
145 2
        if ($this->locked) {
146
            throw new BlocktrailSDKException("Wallet needs to be unlocked to change password");
147
        }
148
149 2
        if (!$this->secret) {
150
            throw new BlocktrailSDKException("No secret");
151
        }
152
153 2
        $encryptedSecret = Encryption::encrypt($this->secret, new Buffer($newPassword));
154
155 2
        $this->sdk->updateWallet($this->identifier, ['encrypted_secret' => base64_encode($encryptedSecret->getBinary())]);
156
157 2
        $this->encryptedSecret = $encryptedSecret;
158
159
        return [
160 2
            'encrypted_secret' => EncryptionMnemonic::encode($encryptedSecret),
161
        ];
162
    }
163
}
164