Completed
Pull Request — master (#88)
by thomas
05:05
created

WalletV2::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1.008

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 12
dl 0
loc 6
ccs 4
cts 5
cp 0.8
crap 1.008
rs 9.4285
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Blocktrail\SDK;
4
5
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKey;
6
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory;
7
use BitWasp\Bitcoin\Mnemonic\MnemonicFactory;
8
use BitWasp\Buffertools\Buffer;
9
use Blocktrail\CryptoJSAES\CryptoJSAES;
10
use Blocktrail\SDK\Bitcoin\BIP32Key;
11
use Blocktrail\SDK\Exceptions\BlocktrailSDKException;
12
use Blocktrail\SDK\Exceptions\WalletDecryptException;
13
14
class WalletV2 extends Wallet {
15
16
    protected $encryptedPrimarySeed;
17
18
    protected $encryptedSecret;
19
20
    protected $secret = null;
21
22
    protected $primarySeed = null;
23
24
    /**
25
     * @param BlocktrailSDKInterface $sdk        SDK instance used to do requests
26
     * @param string                 $identifier identifier of the wallet
27
     * @param string                 $encryptedPrimarySeed
28
     * @param                        $encryptedSecret
29
     * @param BIP32Key[]             $primaryPublicKeys
30
     * @param BIP32Key               $backupPublicKey
31
     * @param BIP32Key[]             $blocktrailPublicKeys
32
     * @param int                    $keyIndex
33
     * @param string                 $network
34
     * @param bool                   $testnet
35
     * @param bool                   $segwit
36
     * @param string                 $checksum
37
     */
38 3
    public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum) {
39 3
        $this->encryptedPrimarySeed = $encryptedPrimarySeed;
40 3
        $this->encryptedSecret = $encryptedSecret;
41
42 3
        parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum);
43
    }
44
45
    /**
46
     * unlock wallet so it can be used for payments
47
     *
48
     * @param          $options ['primary_private_key' => key] OR ['passphrase' => pass]
49
     * @param callable $fn
50
     * @return bool
51
     * @throws \Exception
52
     */
53
    public function unlock($options, callable $fn = null) {
54
        // explode the wallet data
55
        $password = isset($options['passphrase']) ? $options['passphrase'] : (isset($options['password']) ? $options['password'] : null);
56
        $encryptedPrimarySeed = $this->encryptedPrimarySeed;
57
        $encryptedSecret = $this->encryptedSecret;
58
        $primaryPrivateKey = isset($options['primary_private_key']) ? $options['primary_private_key'] : null;
59
60
        if (isset($options['secret'])) {
61
            $this->secret = $options['secret'];
62
        }
63
        if (isset($options['primary_seed'])) {
64
            $this->primarySeed = $options['primary_seed'];
65
        }
66
67
        if (!$primaryPrivateKey) {
68
            if (!$password) {
69
                throw new \InvalidArgumentException("Can't init wallet with Primary Seed without a passphrase");
70
            } elseif (!$encryptedSecret) {
71
                throw new \InvalidArgumentException("Can't init wallet with Primary Seed without a encrypted secret");
72
            }
73
        }
74
75
        if ($primaryPrivateKey) {
76
            if (!($primaryPrivateKey instanceof HierarchicalKey) && !($primaryPrivateKey instanceof BIP32Key)) {
77
                $primaryPrivateKey = HierarchicalKeyFactory::fromExtended($primaryPrivateKey);
78
            }
79
        } else {
80
            if (!($this->secret = CryptoJSAES::decrypt($encryptedSecret, $password))) {
81
                throw new WalletDecryptException("Failed to decrypt secret with password");
82
            }
83
84
            // convert the mnemonic to a seed using BIP39 standard
85
            if (!($this->primarySeed = CryptoJSAES::decrypt($encryptedPrimarySeed, $this->secret))) {
86
                throw new WalletDecryptException("Failed to decrypt primary seed with secret");
87
            }
88
89
            $seedBuffer = new Buffer(base64_decode($this->primarySeed));
90
91
            // create BIP32 private key from the seed
92
            $primaryPrivateKey = HierarchicalKeyFactory::fromEntropy($seedBuffer);
93
        }
94
95
        $this->primaryPrivateKey = $primaryPrivateKey instanceof BIP32Key ? $primaryPrivateKey : BIP32Key::create($primaryPrivateKey, "m");
96
97
        // create checksum (address) of the primary privatekey to compare to the stored checksum
98
        $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress();
99
        if ($checksum != $this->checksum) {
100
            throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password");
101
        }
102
103
        $this->locked = false;
104
105
        // if the response suggests we should upgrade to a different blocktrail cosigning key then we should
106
        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...
107
            $this->upgradeKeyIndex($data['upgrade_key_index']);
108
        }
109
110
        if ($fn) {
111
            $fn($this);
112
            $this->lock();
113
        }
114
    }
115
116
    /**
117
     * lock the wallet (unsets primary private key)
118
     *
119
     * @return void
120
     */
121
    public function lock() {
122
        $this->primaryPrivateKey = null;
123
        $this->secret = null;
124
        $this->primarySeed = null;
125
        $this->locked = true;
126
    }
127
128
    /**
129
     * change password that is used to store data encrypted on server
130
     *
131
     * @param $newPassword
132
     * @return array backupInfo
133
     * @throws BlocktrailSDKException
134
     */
135
    public function passwordChange($newPassword) {
136
        if ($this->locked) {
137
            throw new BlocktrailSDKException("Wallet needs to be unlocked to change password");
138
        }
139
140
        if (!$this->secret) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->secret of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
141
            throw new BlocktrailSDKException("No secret");
142
        }
143
144
        $encryptedSecret = CryptoJSAES::encrypt($this->secret, $newPassword);
145
146
        $this->sdk->updateWallet($this->identifier, ['encrypted_secret' => $encryptedSecret]);
147
148
        $this->encryptedSecret = $encryptedSecret;
149
150
        return [
151
            'encrypted_secret' => MnemonicFactory::bip39()->entropyToMnemonic(new Buffer(base64_decode($this->encryptedSecret))),
152
        ];
153
    }
154
}
155