Пример #1
0
 public function handle_order_tx_submission($order, $incoming_tx, $user_bip32_key)
 {
     $this->CI->load->model('transaction_cache_model');
     $currently_unsigned = strlen($order['partially_signed_transaction']) == 0;
     if ($currently_unsigned) {
         $start_tx = trim($order['unsigned_transaction']);
     } else {
         $start_tx = trim($order['partially_signed_transaction']);
     }
     $json = str_replace("'", '', $order['json_inputs']);
     $decode_current_tx = \BitWasp\BitcoinLib\RawTransaction::decode($start_tx);
     $decode_incoming_tx = \BitWasp\BitcoinLib\RawTransaction::decode($incoming_tx);
     // Does incoming tx match expected spend?
     $check = $this->CI->transaction_cache_model->check_if_expected_spend($decode_incoming_tx['vout'], $order['id']);
     if ($check !== $order['address']) {
         return 'Invalid transaction.';
     }
     // General check that signatures match tx
     $validate = \BitWasp\BitcoinLib\RawTransaction::validate_signed_transaction($incoming_tx, $json);
     if ($validate == FALSE) {
         return 'Invalid signature.';
     }
     $decode_redeem_script = \BitWasp\BitcoinLib\RawTransaction::decode_redeem_script($order['redeemScript']);
     if (!$currently_unsigned and $user_bip32_key['provider'] == 'JS') {
         // Need to build the sig from this tx into the last.
         $copy = $decode_incoming_tx;
         foreach ($copy['vin'] as $i => &$input) {
             $script = explode(" ", $input['scriptSig']['asm']);
             $sig1 = \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($script[1]) / 2) . $script[1];
             $old_script = explode(" ", $decode_current_tx['vin'][$i]['scriptSig']['asm']);
             $sig2 = \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($old_script[1]) / 2) . $old_script[1];
             $redeem_script = '4c' . \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($order['redeemScript']) / 2) . $order['redeemScript'];
             $input['scriptSig']['hex'] = '00' . $sig1 . $sig2 . $redeem_script;
         }
         $incoming_tx = \BitWasp\BitcoinLib\RawTransaction::encode($copy);
         // Now need to reorder sigs!
         $assoc = $this->associate_sigs_with_keys($incoming_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']);
         foreach ($copy['vin'] as $i => &$input) {
             $input['scriptSig']['hex'] = \BitWasp\BitcoinLib\RawTransaction::_apply_sig_scripthash_multisig($assoc[$i], array('public_keys' => $decode_redeem_script['keys'], 'script' => $order['redeemScript']));
         }
         $incoming_tx = \BitWasp\BitcoinLib\RawTransaction::encode($copy);
         $decode_incoming_tx = \BitWasp\BitcoinLib\RawTransaction::decode($incoming_tx);
     }
     // Compare signatures!
     $old_sig_map = $this->associate_sigs_with_keys($start_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']);
     // Now check current signatures against users key. submittee must have signed.
     $key_sig_map = $this->associate_sigs_with_keys($incoming_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']);
     foreach ($key_sig_map as $i => $input_sig_map) {
         // If the number of sigs hasn't increased, or no sig from the current user exists..
         if (count($old_sig_map) > 0 && count($input_sig_map) <= count($old_sig_map[$i]) or !isset($input_sig_map[$user_bip32_key['public_key']])) {
             return 'Incorrect signature!';
         }
     }
     // Broadcast tx if fully signed!
     if (!$currently_unsigned) {
         $this->CI->transaction_cache_model->to_broadcast($incoming_tx);
         $this->sendrawtransaction($incoming_tx);
     }
     return TRUE;
 }
Пример #2
0
 /**
  * verify a signed message
  *
  * @param $address
  * @param $signature
  * @param $message
  * @return bool
  * @throws \Exception
  */
 public static function verifyMessage($address, $signature, $message)
 {
     $math = EccFactory::getAdapter();
     $generator = EccFactory::getSecgCurves($math)->generator256k1();
     // extract parameters
     $address = substr(hex2bin(self::base58_decode($address)), 0, -4);
     if (strlen($address) != 21 || $address[0] != hex2bin(self::magicByte())) {
         throw new \InvalidArgumentException('invalid Bitcoin address');
     }
     $signature = base64_decode($signature, true);
     if ($signature === false) {
         throw new \InvalidArgumentException('invalid base64 signature');
     }
     if (strlen($signature) != 65) {
         throw new \InvalidArgumentException('invalid signature length');
     }
     $recoveryFlags = ord($signature[0]) - 27;
     if ($recoveryFlags < 0 || $recoveryFlags > 7) {
         throw new \InvalidArgumentException('invalid signature type');
     }
     $isCompressed = ($recoveryFlags & 4) != 0;
     // message is <varInt><prefix><varInt><message>
     $messageHash = "Bitcoin Signed Message:\n" . hex2bin(RawTransaction::_encode_vint(strlen($message))) . $message;
     $messageHash = hash('sha256', hash('sha256', $messageHash, true), true);
     try {
         $pubkey = self::recoverPubKey($math->hexDec(bin2hex(substr($signature, 1, 32))), $math->hexDec(bin2hex(substr($signature, 33, 32))), $math->hexDec(bin2hex($messageHash)), $recoveryFlags, $generator);
     } catch (\Exception $e) {
         throw new \Exception("unable to recover key", 0, $e);
     }
     if ($pubkey === false) {
         throw new \Exception('unable to recover key');
     }
     $point = $pubkey->getPoint();
     // see that the key we recovered is for the address given
     if (!$isCompressed) {
         $pubBinStr = "" . str_pad(hex2bin(self::padHex($math->decHex($point->getX()))), 32, "", STR_PAD_LEFT) . str_pad(hex2bin(self::padHex($math->decHex($point->getY()))), 32, "", STR_PAD_LEFT);
     } else {
         $pubBinStr = ($math->mod($point->getY(), 2) == 0 ? "" : "") . str_pad(hex2bin(self::padHex($math->decHex($point->getX()))), 32, "", STR_PAD_LEFT);
     }
     $derivedAddress = hex2bin(self::magicByte()) . hash('ripemd160', hash('sha256', $pubBinStr, true), true);
     return $address === $derivedAddress;
 }