Complex classes like WalletSweeper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WalletSweeper, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 31 | abstract class WalletSweeper { |
||
| 32 | |||
| 33 | /** |
||
| 34 | * network to use - currently only supporting 'bitcoin' |
||
| 35 | * @var string |
||
| 36 | */ |
||
| 37 | protected $network; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * using testnet or not |
||
| 41 | * @var bool |
||
| 42 | */ |
||
| 43 | protected $testnet; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * backup private key |
||
| 47 | * @var BIP32Key |
||
| 48 | */ |
||
| 49 | protected $backupPrivateKey; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * primary private key |
||
| 53 | * @var BIP32Key |
||
| 54 | */ |
||
| 55 | protected $primaryPrivateKey; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * blocktrail public keys, mapped to their relevant keyIndex |
||
| 59 | * @var |
||
| 60 | */ |
||
| 61 | protected $blocktrailPublicKeys; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * gets unspent outputs for addresses |
||
| 65 | * @var UnspentOutputFinder |
||
| 66 | */ |
||
| 67 | protected $utxoFinder; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * holds wallet addresses, along with path, redeem script, and discovered unspent outputs |
||
| 71 | * @var array |
||
| 72 | */ |
||
| 73 | protected $sweepData; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * @var AddressReaderBase |
||
| 77 | */ |
||
| 78 | protected $addressReader; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * process logging for debugging |
||
| 82 | * @var bool |
||
| 83 | */ |
||
| 84 | protected $debug = false; |
||
| 85 | |||
| 86 | /** |
||
| 87 | * @param BufferInterface $primarySeed |
||
| 88 | * @param BufferInterface $backupSeed |
||
| 89 | * @param array $blocktrailPublicKeys = |
||
| 90 | * @param UnspentOutputFinder $utxoFinder |
||
| 91 | * @param string $network |
||
| 92 | * @param bool $testnet |
||
| 93 | * @throws \Exception |
||
| 94 | */ |
||
| 95 | public function __construct(BufferInterface $primarySeed, BufferInterface $backupSeed, array $blocktrailPublicKeys, UnspentOutputFinder $utxoFinder, $network = 'btc', $testnet = false) { |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @param array $options |
||
| 117 | * @return AddressReaderBase |
||
| 118 | */ |
||
| 119 | private function makeAddressReader(array $options) { |
||
| 130 | |||
| 131 | /** |
||
| 132 | * set BitcoinLib to the correct magic-byte defaults for the selected network |
||
| 133 | * |
||
| 134 | * @param $network |
||
| 135 | * @param $testnet |
||
| 136 | */ |
||
| 137 | protected function setBitcoinLibMagicBytes($network, $testnet, $regtest) { |
||
| 138 | assert($network == "bitcoin" || $network == "bitcoincash"); |
||
| 139 | if ($network === "bitcoin") { |
||
| 140 | if ($regtest) { |
||
| 141 | $useNetwork = NetworkFactory::bitcoinRegtest(); |
||
| 142 | } else if ($testnet) { |
||
| 143 | $useNetwork = NetworkFactory::bitcoinTestnet(); |
||
| 144 | } else { |
||
| 145 | $useNetwork = NetworkFactory::bitcoin(); |
||
| 146 | } |
||
| 147 | } else if ($network === "bitcoincash") { |
||
| 148 | if ($regtest) { |
||
| 149 | $useNetwork = new BitcoinCashRegtest(); |
||
| 150 | } else if ($testnet) { |
||
| 151 | $useNetwork = new BitcoinCashTestnet(); |
||
| 152 | } else { |
||
| 153 | $useNetwork = new BitcoinCash(); |
||
| 154 | } |
||
| 155 | } |
||
| 156 | |||
| 157 | Bitcoin::setNetwork($useNetwork); |
||
|
|
|||
| 158 | } |
||
| 159 | |||
| 160 | /** |
||
| 161 | * enable debug info logging (just to console) |
||
| 162 | */ |
||
| 163 | public function enableLogging() { |
||
| 167 | |||
| 168 | /** |
||
| 169 | * disable debug info logging |
||
| 170 | */ |
||
| 171 | public function disableLogging() { |
||
| 175 | |||
| 176 | /** |
||
| 177 | * normalize network string |
||
| 178 | * |
||
| 179 | * @param $network |
||
| 180 | * @param $testnet |
||
| 181 | * @return array |
||
| 182 | * @throws \Exception |
||
| 183 | */ |
||
| 184 | protected function normalizeNetwork($network, $testnet) { |
||
| 187 | |||
| 188 | /** |
||
| 189 | * generate multisig address for given path |
||
| 190 | * |
||
| 191 | * @param string|BIP32Path $path |
||
| 192 | * @return array[string, ScriptInterface] [address, redeemScript] |
||
| 193 | * @throws \Exception |
||
| 194 | */ |
||
| 195 | protected function createAddress($path) { |
||
| 216 | |||
| 217 | /** |
||
| 218 | * create a batch of multisig addresses |
||
| 219 | * |
||
| 220 | * @param $start |
||
| 221 | * @param $count |
||
| 222 | * @param $keyIndex |
||
| 223 | * @return array |
||
| 224 | */ |
||
| 225 | protected function createBatchAddresses($start, $count, $keyIndex) { |
||
| 246 | |||
| 247 | |||
| 248 | /** |
||
| 249 | * gets the blocktrail pub key for the given path from the stored array of pub keys |
||
| 250 | * |
||
| 251 | * @param string|BIP32Path $path |
||
| 252 | * @return BIP32Key |
||
| 253 | * @throws \Exception |
||
| 254 | */ |
||
| 255 | protected function getBlocktrailPublicKey($path) { |
||
| 266 | |||
| 267 | /** |
||
| 268 | * discover funds in the wallet |
||
| 269 | * |
||
| 270 | * @param int $increment how many addresses to scan at a time |
||
| 271 | * @return array |
||
| 272 | * @throws BlocktrailSDKException |
||
| 273 | */ |
||
| 274 | public function discoverWalletFunds($increment = 200) { |
||
| 337 | |||
| 338 | /** |
||
| 339 | * sweep the wallet of all funds and send to a single address |
||
| 340 | * |
||
| 341 | * @param string $destinationAddress address to receive found funds |
||
| 342 | * @param int $sweepBatchSize number of addresses to search at a time |
||
| 343 | * @return string HEX string of signed transaction |
||
| 344 | * @throws \Exception |
||
| 345 | */ |
||
| 346 | public function sweepWallet($destinationAddress, $sweepBatchSize = 200) { |
||
| 367 | |||
| 368 | /** |
||
| 369 | * @param $destinationAddress |
||
| 370 | * @return TransactionInterface |
||
| 371 | */ |
||
| 372 | protected function createTransaction($destinationAddress) { |
||
| 424 | |||
| 425 | /** |
||
| 426 | * sign a raw transaction with the private keys that we have |
||
| 427 | * |
||
| 428 | * @param TransactionInterface $tx |
||
| 429 | * @param SignInfo[] $signInfo |
||
| 430 | * @return TransactionInterface |
||
| 431 | * @throws \Exception |
||
| 432 | */ |
||
| 433 | protected function signTransaction(TransactionInterface $tx, array $signInfo) { |
||
| 471 | } |
||
| 472 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: