<?php use BitWasp\BitcoinLib\BitcoinLib; require_once __DIR__ . '/../vendor/autoload.php'; $magic_byte = '00'; $keypair = BitcoinLib::get_new_key_set($magic_byte); echo "Key pair: \n"; print_r($keypair); echo "\n"; $compress = BitcoinLib::compress_public_key($keypair['pubKey']); echo "Compressed public key: {$compress} \n"; $decompress = BitcoinLib::decompress_public_key($compress); echo "Decompressed key info: \n"; print_r($decompress); echo "\n"; $address = BitcoinLib::public_key_to_address($compress, $magic_byte); echo "decoding {$address}\n"; echo BitcoinLib::base58_decode($address); echo "\n\n"; $sc = '5357'; $ad = BitcoinLib::public_key_to_address($sc, '05'); echo $ad . "\n";
public function testPublicKeyCompression() { $cnt = (getenv('BITCOINLIB_EXTENSIVE_TESTING') ?: 1) * 2; for ($i = 0; $i < $cnt; $i++) { $key = BitcoinLib::get_new_private_key(); $public = BitcoinLib::private_key_to_public_key($key, FALSE); $compress = BitcoinLib::compress_public_key($public); $decompress = BitcoinLib::decompress_public_key($compress); $this->assertTrue($decompress['public_key'] == $public); } }
/** * Private Keys To Wallet * * This function accepts $wallet - a reference to an array containing * wallet info, indexed by hash160 of expected address. * It will attempt to add each key to this wallet, as well as all the * details that could be needed later on: public key, uncompressed key, * address, an indicator for address compression. Type is always set * to pubkeyhash for private key entries in the wallet. * * @param array $wallet * @param array $wifs * @param string $magic_byte */ public static function private_keys_to_wallet(&$wallet, array $wifs, $magic_byte = null) { $magic_byte = BitcoinLib::magicByte($magic_byte); if (count($wifs) > 0) { foreach ($wifs as $wif) { if (is_array($wif) && isset($wif['key'], $wif['is_compressed'])) { $key = $wif; } else { $key = BitcoinLib::WIF_to_private_key($wif); } $pubkey = BitcoinLib::private_key_to_public_key($key['key'], $key['is_compressed']); if ($pubkey !== false) { $pk_hash = BitcoinLib::hash160($pubkey); if ($key['is_compressed'] == true) { $uncompressed_key = BitcoinLib::decompress_public_key($pubkey); $uncompressed_key = $uncompressed_key['public_key']; } else { $uncompressed_key = $pubkey; } $wallet[$pk_hash] = array('type' => 'pubkeyhash', 'private_key' => $key['key'], 'public_key' => $pubkey, 'uncompressed_key' => $uncompressed_key, 'is_compressed' => $key['is_compressed'], 'address' => BitcoinLib::hash160_to_address($pk_hash, $magic_byte)); } } } }
/** * CKD * * This recursive function accepts $master, a parent extended key, * and an array of address bytes (the $address_definition tuple). It * pop's the next value from the $address_definition tuple and * generates the desired key. If the $address_definition tuple is * empty, then it returns the key. If not, then it calls itself again * with the new key and the tuple with the remaining key indexes to * generate, but will terminate with an array containing the desired * key at index 0, and it's human readable definition in the second. * * @param string $master * @param array $address_definition * @param array $generated (internal, should never be set in your code) * @return array */ public static function CKD($master, $address_definition, $generated = array()) { $previous = self::import($master); if ($previous['type'] == 'private') { $private_key = $previous['key']; $public_key = null; } else { $private_key = null; $public_key = $previous['key']; } $i = array_pop($address_definition); $is_prime = self::check_is_prime_hex($i); if ($is_prime == 1) { if ($previous['type'] == 'public') { throw new \InvalidArgumentException("Can't derive private key from public key"); } $data = '00' . $private_key . $i; } else { $public_key = $public_key ?: BitcoinLib::private_key_to_public_key($private_key, true); $data = $public_key . $i; } /* * optimization; * if this isn't the last derivation then the fingerprint is irrelevant so we can just spoof it! * that way we don't need the public key for the fingerprint */ if (empty($address_definition)) { $public_key = $public_key ?: BitcoinLib::private_key_to_public_key($private_key, true); $fingerprint = substr(hash('ripemd160', hash('sha256', pack("H*", $public_key), true)), 0, 8); } else { $fingerprint = "FFFFFFFF"; } $I = hash_hmac('sha512', pack("H*", $data), pack("H*", $previous['chain_code'])); $I_l = substr($I, 0, 64); $I_r = substr($I, 64, 64); if (self::check_valid_hmac_key($I_l) == false) { // Check the key is in a valid range. // calculate the next i in the sequence, and start over with that. $new_i = self::calc_address_bytes(self::get_address_number($i) + 1, $is_prime); array_push($address_definition, $new_i); return self::CKD($master, $address_definition, $generated); } // Keep a record of the address being built. Done after error // checking so only valid keys get to this point. if (count($generated) == 0 && $previous['depth'] == 0) { array_push($generated, $previous['type'] == 'private' ? 'm' : 'M'); } array_push($generated, self::get_address_number($i, $is_prime) . ($is_prime == 1 ? "'" : null)); $math = EccFactory::getAdapter(); $g = EccFactory::getSecgCurves($math)->generator256k1(); $n = $g->getOrder(); $Il_dec = $math->hexDec($I_l); if ($previous['type'] == 'private') { $private_key_dec = $math->hexDec($private_key); $key_dec = $math->mod($math->add($Il_dec, $private_key_dec), $n); $key = str_pad(BitcoinLib::hex_encode($key_dec), 64, '0', STR_PAD_LEFT); } else { if ($previous['type'] == 'public') { // newPoint + parentPubkeyPoint $decompressed = BitcoinLib::decompress_public_key($public_key); // Can return false. Throw exception? $new_point = $g->mul($Il_dec)->add($decompressed['point']); // Prepare offset, by multiplying Il by g, and adding this to the previous public key point. // Create a new point by adding the two. $new_x = str_pad(BitcoinLib::hex_encode($new_point->getX()), 64, '0', STR_PAD_LEFT); $new_y = str_pad(BitcoinLib::hex_encode($new_point->getY()), 64, '0', STR_PAD_LEFT); $key = BitcoinLib::compress_public_key('04' . $new_x . $new_y); } else { throw new \InvalidArgumentException("Unknown previous type in CKD"); } } $data = array('network' => $previous['network'], 'testnet' => $previous['testnet'], 'magic_bytes' => $previous['magic_bytes'], 'type' => $previous['type'], 'depth' => $previous['depth'] + 1, 'fingerprint' => $fingerprint, 'i' => $i, 'address_number' => self::get_address_number($i), 'chain_code' => $I_r, 'key' => $key); return count($address_definition) > 0 ? self::CKD(self::encode($data), $address_definition, $generated) : array(self::encode($data), implode('/', $generated)); }