Esempio n. 1
0
 /**
  * Prime the address with UTXOs of a certain size
  *   only if needed
  * 
  *
  * @return Response
  */
 public function primeAddress($address_uuid, Guard $auth, Request $request, APIControllerHelper $helper, PaymentAddressRepository $payment_address_repository, TXORepository $txo_repository, TXOChooser $txo_chooser, PaymentAddressSender $payment_address_sender)
 {
     try {
         $user = $auth->getUser();
         if (!$user) {
             throw new Exception("User not found", 1);
         }
         $payment_address = $helper->requireResourceOwnedByUser($address_uuid, $user, $payment_address_repository);
         $size = floatval($request->input('size'));
         if ($size <= 0) {
             throw new Exception("Invalid size", 400);
         }
         $size_satoshis = CurrencyUtil::valueToSatoshis($size);
         $desired_prime_count = floatval($request->input('count'));
         if ($desired_prime_count <= 0) {
             throw new Exception("Invalid count", 400);
         }
         $fee = $request->input('fee');
         if ($fee !== null) {
             $fee = floatval($fee);
             if ($fee <= 0) {
                 throw new Exception("Invalid fee", 400);
             }
         }
         // get the UTXOs
         //   [TXO::UNCONFIRMED, TXO::CONFIRMED]
         $txos = $this->filterGreenOrConfirmedUTXOs($txo_repository->findByPaymentAddress($payment_address, null, true));
         // count the number that match the size
         $current_primed_count = 0;
         foreach ($txos as $txo) {
             if ($txo['amount'] == $size_satoshis) {
                 ++$current_primed_count;
             }
         }
         $txid = null;
         $new_primed_count = $current_primed_count;
         if ($desired_prime_count > $current_primed_count) {
             // create a new priming transaction...
             $desired_new_primes_count_to_create = $desired_prime_count - $current_primed_count;
             $actual_new_primes_count_to_create = $this->findMaximumNewPrimeCountTXOs($txo_chooser, $payment_address, $desired_new_primes_count_to_create, $size, $fee);
             if ($actual_new_primes_count_to_create > 0) {
                 $txid = $this->sendPrimingTransaction($payment_address_sender, $payment_address, $size, $actual_new_primes_count_to_create, $fee);
                 $new_primed_count = $current_primed_count + $actual_new_primes_count_to_create;
             }
         }
         $output = ['oldPrimedCount' => $current_primed_count, 'newPrimedCount' => $new_primed_count, 'txid' => $txid, 'primed' => $txid ? true : false];
         return $helper->buildJSONResponse($output);
     } catch (Exception $e) {
         if ($e->getCode() >= 400 and $e->getCode() < 500) {
             throw new HttpResponseException(new JsonResponse(['errors' => [$e->getMessage()]], 400));
         }
         throw $e;
     }
 }
 public function transfer($address_uuid, Guard $auth, AccountTransferRequest $request, APIControllerHelper $helper, PaymentAddressRepository $payment_address_repository, APICallRepository $api_call_repository)
 {
     $user = $auth->getUser();
     if (!$user) {
         throw new Exception("User not found", 1);
     }
     $payment_address = $helper->requireResourceOwnedByUser($address_uuid, $user, $payment_address_repository);
     $params = $helper->getAttributesFromRequest($request);
     $api_call = $api_call_repository->create(['user_id' => $user['id'], 'details' => ['method' => 'api/v1/accounts/transfer/' . $address_uuid, 'args' => $params]]);
     try {
         if (isset($params['close']) and $params['close']) {
             AccountHandler::close($payment_address, $params['from'], $params['to'], $api_call);
         } else {
             if (isset($params['quantity']) and isset($params['asset'])) {
                 AccountHandler::transfer($payment_address, $params['from'], $params['to'], $params['quantity'], $params['asset'], isset($params['txid']) ? $params['txid'] : null, $api_call);
             } else {
                 // transfer all
                 AccountHandler::transferAllByTIXD($payment_address, $params['from'], $params['to'], $params['txid'], $api_call);
             }
         }
         // done
         return $helper->buildJSONResponse([], 204);
     } catch (AccountException $e) {
         return $helper->buildJSONResponse(['message' => $e->getMessage(), 'errorName' => $e->getErrorName()], $e->getStatusCode());
     } catch (HttpException $e) {
         return $helper->buildJSONResponse(['message' => $e->getMessage()], $e->getStatusCode());
     }
 }
Esempio n. 3
0
 /**
  * Remove the specified resource from storage.
  *
  * @param  int  $id
  * @return Response
  */
 public function destroy(APIControllerHelper $helper, AccountRepository $account_respository, $id)
 {
     return $helper->destroy($account_respository, $id);
 }
Esempio n. 4
0
 protected function executeSend(APIControllerHelper $helper, Request $request, PaymentAddressRepository $payment_address_respository, SendRepository $send_respository, PaymentAddressSender $address_sender, Guard $auth, APICallRepository $api_call_repository, $id)
 {
     $user = $auth->getUser();
     if (!$user) {
         throw new Exception("User not found", 1);
     }
     // get the address
     $payment_address = $payment_address_respository->findByUuid($id);
     if (!$payment_address) {
         return new JsonResponse(['message' => 'address not found'], 404);
     }
     // make sure this address belongs to this user
     if ($payment_address['user_id'] != $user['id']) {
         return new JsonResponse(['message' => 'Not authorized to send from this address'], 403);
     }
     // attributes
     $request_attributes = $request->only(array_keys($request->rules()));
     // determine if this is a multisend
     $is_multisend = (isset($request_attributes['destinations']) and $request_attributes['destinations']);
     $is_regular_send = !$is_multisend;
     // normalize destinations
     $destinations = $is_multisend ? $this->normalizeDestinations($request_attributes['destinations']) : '';
     $destination = $is_regular_send ? $request_attributes['destination'] : '';
     // determine variables
     $quantity_sat = CurrencyUtil::valueToSatoshis($is_multisend ? $this->sumMultisendQuantity($destinations) : $request_attributes['quantity']);
     $asset = $is_regular_send ? $request_attributes['asset'] : 'BTC';
     $is_sweep = isset($request_attributes['sweep']) ? !!$request_attributes['sweep'] : false;
     $float_fee = isset($request_attributes['fee']) ? $request_attributes['fee'] : PaymentAddressSender::DEFAULT_FEE;
     $dust_size = isset($request_attributes['dust_size']) ? $request_attributes['dust_size'] : PaymentAddressSender::DEFAULT_REGULAR_DUST_SIZE;
     $request_id = isset($request_attributes['requestId']) ? $request_attributes['requestId'] : Uuid::uuid4()->toString();
     // create attibutes
     $create_attributes = [];
     $create_attributes['user_id'] = $user['id'];
     $create_attributes['payment_address_id'] = $payment_address['id'];
     $create_attributes['destination'] = $destination;
     $create_attributes['quantity_sat'] = $quantity_sat;
     $create_attributes['asset'] = $asset;
     $create_attributes['is_sweep'] = $is_sweep;
     $create_attributes['fee'] = $float_fee;
     $create_attributes['dust_size'] = $dust_size;
     // for multisends
     $create_attributes['destinations'] = $destinations;
     // the transaction must be committed before the lock is release and not after
     //   therefore we must release the lock after this closure completes
     $lock_must_be_released = false;
     $lock_must_be_released_with_delay = false;
     // create a send and lock it immediately
     $send_result = $send_respository->executeWithNewLockedSendByRequestID($request_id, $create_attributes, function ($locked_send) use($request_attributes, $create_attributes, $payment_address, $user, $helper, $send_respository, $address_sender, $api_call_repository, $request_id, $is_multisend, $is_regular_send, $quantity_sat, $asset, $destination, $destinations, $is_sweep, $float_fee, $dust_size, &$lock_must_be_released, &$lock_must_be_released_with_delay) {
         $api_call = $api_call_repository->create(['user_id' => $user['id'], 'details' => ['method' => 'api/v1/sends/' . $payment_address['uuid'], 'args' => $request_attributes]]);
         // if a send already exists by this request_id, just return it
         if (isset($locked_send['txid']) && strlen($locked_send['txid'])) {
             EventLog::log('send.alreadyFound', $locked_send);
             return $helper->transformResourceForOutput($locked_send);
         }
         $float_quantity = CurrencyUtil::satoshisToValue($quantity_sat);
         // send
         EventLog::log('send.requested', array_merge($request_attributes, $create_attributes));
         if ($is_sweep) {
             try {
                 // get lock
                 $lock_acquired = AccountHandler::acquirePaymentAddressLock($payment_address);
                 if ($lock_acquired) {
                     $lock_must_be_released = true;
                 }
                 list($txid, $float_balance_sent) = $address_sender->sweepAllAssets($payment_address, $request_attributes['destination'], $float_fee);
                 $quantity_sat_sent = CurrencyUtil::valueToSatoshis($float_balance_sent);
                 // clear all balances from all accounts
                 AccountHandler::zeroAllBalances($payment_address, $api_call);
                 // release the account lock with a slight delay
                 if ($lock_acquired) {
                     $lock_must_be_released_with_delay = true;
                 }
             } catch (PaymentException $e) {
                 EventLog::logError('error.sweep', $e);
                 return new JsonResponse(['message' => $e->getMessage()], 500);
             } catch (Exception $e) {
                 EventLog::logError('error.sweep', $e);
                 return new JsonResponse(['message' => 'Unable to complete this request'], 500);
             }
         } else {
             try {
                 // get the account
                 $account_name = (isset($request_attributes['account']) and strlen($request_attributes['account'])) ? $request_attributes['account'] : 'default';
                 $account = AccountHandler::getAccount($payment_address, $account_name);
                 if (!$account) {
                     EventLog::logError('error.send.accountMissing', ['address_id' => $payment_address['id'], 'account' => $account_name]);
                     return new JsonResponse(['message' => "This account did not exist."], 404);
                 }
                 // Log::debug("\$account=".json_encode($account, 192));
                 // get lock
                 $lock_acquired = AccountHandler::acquirePaymentAddressLock($payment_address);
                 if ($lock_acquired) {
                     $lock_must_be_released = true;
                 }
                 // whether to spend unconfirmed balances
                 $allow_unconfirmed = isset($request_attributes['unconfirmed']) ? $request_attributes['unconfirmed'] : false;
                 // Log::debug("\$allow_unconfirmed=".json_encode($allow_unconfirmed, 192));
                 // validate that the funds are available
                 if ($allow_unconfirmed) {
                     $has_enough_funds = AccountHandler::accountHasSufficientFunds($account, $float_quantity, $asset, $float_fee, $dust_size);
                 } else {
                     $has_enough_funds = AccountHandler::accountHasSufficientConfirmedFunds($account, $float_quantity, $asset, $float_fee, $dust_size);
                 }
                 if (!$has_enough_funds) {
                     EventLog::logError('error.send.insufficient', ['address_id' => $payment_address['id'], 'account' => $account_name, 'quantity' => $float_quantity, 'asset' => $asset]);
                     return new JsonResponse(['message' => "This account does not have sufficient" . ($allow_unconfirmed ? '' : ' confirmed') . " funds available."], 400);
                 }
                 // send the funds
                 EventLog::log('send.begin', ['request_id' => $request_id, 'address_id' => $payment_address['id'], 'account' => $account_name, 'quantity' => $float_quantity, 'asset' => $asset, 'destination' => $is_multisend ? $destinations : $destination]);
                 $txid = $address_sender->sendByRequestID($request_id, $payment_address, $is_multisend ? $destinations : $destination, $float_quantity, $asset, $float_fee, $dust_size);
                 EventLog::log('send.complete', ['txid' => $txid, 'request_id' => $request_id, 'address_id' => $payment_address['id'], 'account' => $account_name, 'quantity' => $float_quantity, 'asset' => $asset, 'destination' => $is_multisend ? $destinations : $destination]);
                 // tag funds as sent with the txid
                 if ($allow_unconfirmed) {
                     AccountHandler::markAccountFundsAsSending($account, $float_quantity, $asset, $float_fee, $dust_size, $txid);
                 } else {
                     AccountHandler::markConfirmedAccountFundsAsSending($account, $float_quantity, $asset, $float_fee, $dust_size, $txid);
                     // Log::debug("After marking confirmed funds as sent, all accounts for ${account['name']}: ".json_encode(app('App\Repositories\LedgerEntryRepository')->accountBalancesByAsset($account, null), 192));
                     // Log::debug("After marking confirmed funds as sent, all accounts for default: ".json_encode(app('App\Repositories\LedgerEntryRepository')->accountBalancesByAsset(AccountHandler::getAccount($payment_address), null), 192));
                 }
                 // release the account lock
                 if ($lock_acquired) {
                     $lock_must_be_released_with_delay = true;
                 }
             } catch (AccountException $e) {
                 EventLog::logError('error.pay', $e);
                 return new JsonResponse(['message' => $e->getMessage(), 'errorName' => $e->getErrorName()], $e->getStatusCode());
             } catch (PaymentException $e) {
                 EventLog::logError('error.pay', $e);
                 return new JsonResponse(['message' => $e->getMessage()], 500);
             } catch (Exception $e) {
                 EventLog::logError('error.pay', $e);
                 return new JsonResponse(['message' => 'Unable to complete this request'], 500);
             }
         }
         $attributes = [];
         $attributes['sent'] = time();
         $attributes['txid'] = $txid;
         EventLog::log('send.complete', $attributes);
         // update and send response
         $send_respository->update($locked_send, $attributes);
         return $helper->buildJSONResponse($locked_send->serializeForAPI());
     }, self::SEND_LOCK_TIMEOUT);
     // make sure to release the lock
     if ($lock_must_be_released_with_delay) {
         $this->releasePaymentAddressLockWithDelay($payment_address);
     } else {
         if ($lock_must_be_released) {
             AccountHandler::releasePaymentAddressLock($payment_address);
         }
     }
     return $send_result;
 }
 /**
  * Remove the specified resource from storage.
  *
  * @param  int  $id
  * @return Response
  */
 public function destroy(APIControllerHelper $helper, PaymentAddressRepository $payment_address_respository, $id)
 {
     return $helper->destroy($payment_address_respository, $id);
 }
Esempio n. 6
0
 /**
  * Remove the specified resource from storage.
  *
  * @param  int  $id
  * @return Response
  */
 public function destroy(APIControllerHelper $helper, MonitoredAddressRepository $address_respository, $id)
 {
     return $helper->destroy($address_respository, $id);
 }