/** * Queries the server to determine if a username is available. */ tABC_CC ABC_LoginServerAvailable(const Lobby &lobby, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_ACCOUNT_AVAILABLE; ServerReplyJson replyJson; std::string get; AccountAvailableJson json; // create the json ABC_CHECK_NEW(json.authIdSet(base64Encode(lobby.authId()))); ABC_CHECK_NEW(json.encode(get)); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, get)); // decode the result ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: return cc; }
static tABC_CC ABC_WalletServerRepoPost(const Lobby &lobby, DataSlice LP1, const std::string &szWalletAcctKey, const char *szPath, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; const auto url = ABC_SERVER_ROOT "/" + std::string(szPath); HttpReply reply; ServerReplyJson replyJson; JsonPtr json(json_pack("{ssssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_REPO_WALLET_FIELD, szWalletAcctKey.c_str())); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, json.encode())); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: return cc; }
/** * Activate an account on the server. * * @param LP1 Password hash for the account */ tABC_CC ABC_LoginServerActivate(const Lobby &lobby, tABC_U08Buf LP1, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_ACCOUNT_ACTIVATE; ServerReplyJson replyJson; char *szPost = NULL; json_t *pJSON_Root = NULL; // create the post data pJSON_Root = json_pack("{ssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str()); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); // decode the result ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
/** * Changes the password for an account on the server. * * This function sends information to the server to change the password for an account. * Either the old LP1 or LRA1 can be used for authentication. * * @param oldLP1 Old password hash for the account (if this is empty, LRA1 is used instead) * @param LRA1 LRA1 for the account (used if oldP1 is empty) */ tABC_CC ABC_LoginServerChangePassword(const Lobby &lobby, tABC_U08Buf oldLP1, tABC_U08Buf newLP1, tABC_U08Buf newLRA1, const CarePackage &carePackage, const LoginPackage &loginPackage, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_CHANGE_PASSWORD_PATH; ServerReplyJson replyJson; char *szPost = NULL; std::string carePackageStr; std::string loginPackageStr; json_t *pJSON_OldLRA1 = NULL; json_t *pJSON_NewLRA1 = NULL; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(oldLP1); ABC_CHECK_NULL_BUF(newLP1); ABC_CHECK_NEW(carePackage.encode(carePackageStr)); ABC_CHECK_NEW(loginPackage.encode(loginPackageStr)); // Encode those: pJSON_Root = json_pack("{ss, ss, ss, ss, ss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(oldLP1).c_str(), ABC_SERVER_JSON_NEW_LP1_FIELD, base64Encode(newLP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackageStr.c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackageStr.c_str()); // set up the recovery, if any: if (newLRA1.size()) { pJSON_NewLRA1 = json_string(base64Encode(newLRA1).c_str()); json_object_set(pJSON_Root, ABC_SERVER_JSON_NEW_LRA1_FIELD, pJSON_NewLRA1); } // create the post data szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_OldLRA1) json_decref(pJSON_OldLRA1); if (pJSON_NewLRA1) json_decref(pJSON_NewLRA1); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
/** * Uploads the pin package. * * @param LP1 Login + Password hash * @param DID Device Id * @param LPIN1 Hashed pin * @param szPinPackage Pin package * @param szAli auto-logout interval */ tABC_CC ABC_LoginServerUpdatePinPackage(const Lobby &lobby, tABC_U08Buf LP1, tABC_U08Buf DID, tABC_U08Buf LPIN1, const std::string &pinPackage, time_t ali, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_PIN_PACK_UPDATE_PATH; ServerReplyJson replyJson; char *szPost = NULL; json_t *pJSON_Root = NULL; char szALI[DATETIME_LENGTH]; ABC_CHECK_NULL_BUF(LP1); ABC_CHECK_NULL_BUF(DID); ABC_CHECK_NULL_BUF(LPIN1); // format the ali strftime(szALI, DATETIME_LENGTH, "%Y-%m-%dT%H:%M:%S", gmtime(&ali)); // Encode those: pJSON_Root = json_pack("{ss, ss, ss, ss, ss, ss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_DID_FIELD, base64Encode(DID).c_str(), ABC_SERVER_JSON_LPIN1_FIELD, base64Encode(LPIN1).c_str(), JSON_ACCT_PIN_PACKAGE, pinPackage.c_str(), ABC_SERVER_JSON_ALI_FIELD, szALI); // create the post data szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
static tABC_CC ABC_LoginPasswordDisk(std::shared_ptr<Login> &result, std::shared_ptr<Lobby> lobby, tABC_U08Buf LP, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::unique_ptr<Login> login; tABC_CarePackage *pCarePackage = NULL; tABC_LoginPackage *pLoginPackage = NULL; AutoU08Buf LP2; AutoU08Buf MK; // Load the packages: ABC_CHECK_RET(ABC_LoginDirLoadPackages(lobby->dir(), &pCarePackage, &pLoginPackage, pError)); // Decrypt MK: ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP2, &LP2, pError)); ABC_CHECK_RET(ABC_CryptoDecryptJSONObject(pLoginPackage->EMK_LP2, LP2, &MK, pError)); // Decrypt SyncKey: login.reset(new Login(lobby, static_cast<U08Buf>(MK))); ABC_CHECK_NEW(login->init(pLoginPackage), pError); result.reset(login.release()); exit: ABC_CarePackageFree(pCarePackage); ABC_LoginPackageFree(pLoginPackage); return cc; }
/** * Modifies a previously created receive request. * Note: the previous details will be free'ed so if the user is using the previous details for this request * they should not assume they will be valid after this call. * * @param szRequestID ID of this request * @param pDetails Pointer to transaction details * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_TxModifyReceiveRequest(Wallet &self, const char *szRequestID, tABC_TxDetails *pDetails, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; AutoCoreLock lock(gCoreMutex); Address address; ABC_CHECK_NEW(self.addresses.get(address, szRequestID)); address.metadata = pDetails; ABC_CHECK_NEW(self.addresses.save(address)); exit: return cc; }
tABC_CC ABC_BridgeSweepKey(Wallet &self, tABC_U08Buf key, bool compressed, tABC_Sweep_Done_Callback fCallback, void *pData, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; bc::ec_secret ec_key; bc::ec_point ec_addr; bc::payment_address address; PendingSweep sweep; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); // Decode key and address: ABC_CHECK_ASSERT(key.size() == ec_key.size(), ABC_CC_Error, "Bad key size"); std::copy(key.begin(), key.end(), ec_key.data()); ec_addr = bc::secret_to_public_key(ec_key, compressed); address.set(pubkeyVersion(), bc::bitcoin_short_hash(ec_addr)); // Start the sweep: sweep.address = address; sweep.key = abcd::wif_key{ec_key, compressed}; sweep.done = false; sweep.fCallback = fCallback; sweep.pData = pData; watcherInfo->sweeping.push_back(sweep); watcherInfo->watcher.watch_address(address); exit: return cc; }
tABC_CC ABC_BridgeWatcherConnect(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_GeneralInfo *ppInfo = NULL; const char *szServer = FALLBACK_OBELISK; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); // Pick a server: if (isTestnet()) { szServer = TESTNET_OBELISK; } else if (ABC_CC_Ok == ABC_GeneralGetInfo(&ppInfo, pError) && 0 < ppInfo->countObeliskServers) { ++gLastObelisk; if (ppInfo->countObeliskServers <= gLastObelisk) gLastObelisk = 0; szServer = ppInfo->aszObeliskServers[gLastObelisk]; } // Connect: ABC_DebugLog("Wallet %s connecting to %s", self.id().c_str(), szServer); watcher->connect(szServer); exit: ABC_GeneralFreeInfo(ppInfo); return cc; }
tABC_CC ABC_BridgePrioritizeAddress(Wallet &self, const char *szAddress, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; bc::payment_address addr; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); if (szAddress) { if (!addr.set_encoded(szAddress)) { cc = ABC_CC_Error; ABC_DebugLog("Invalid szAddress %s\n", szAddress); goto exit; } } watcher->prioritize_address(addr); exit: return cc; }
/** * Filters a transaction list, removing any that aren't found in the * watcher database. * @param aTransactions The array to filter. This will be modified in-place. * @param pCount The array length. This will be updated upon return. */ tABC_CC ABC_BridgeFilterTransactions(Wallet &self, tABC_TxInfo **aTransactions, unsigned int *pCount, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_TxInfo *const *end = aTransactions + *pCount; tABC_TxInfo *const *si = aTransactions; tABC_TxInfo **di = aTransactions; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); while (si < end) { tABC_TxInfo *pTx = *si++; int height; bc::hash_digest txid; if (!bc::decode_hash(txid, pTx->szMalleableTxId)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); if (watcher->get_tx_height(txid, height)) { *di++ = pTx; } else { ABC_TxFreeTransaction(pTx); } } *pCount = di - aTransactions; exit: return cc; }
/** * Marks the address as unusable and returns its metadata. * * @param ppDetails Metadata extracted from the address database * @param paAddress Addresses that will be updated * @param addressCount Number of address in paAddress */ tABC_CC ABC_TxTrashAddresses(Wallet &self, tABC_TxDetails **ppDetails, tABC_TxOutput **paAddresses, unsigned int addressCount, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; *ppDetails = nullptr; for (unsigned i = 0; i < addressCount; ++i) { if (paAddresses[i]->input) continue; Address address; if (self.addresses.get(address, paAddresses[i]->szAddress)) { // Update the transaction: if (address.recyclable) { address.recyclable = false; ABC_CHECK_NEW(self.addresses.save(address)); } // Return our details: ABC_TxDetailsFree(*ppDetails); *ppDetails = address.metadata.toDetails(); } } exit: return cc; }
tABC_CC ABC_LoginServerGetPinPackage(tABC_U08Buf DID, tABC_U08Buf LPIN1, char **szPinPackage, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_PIN_PACK_GET_PATH; ServerReplyJson replyJson; json_t *pJSON_Value = NULL; json_t *pJSON_Root = NULL; char *szPost = NULL; ABC_CHECK_NULL_BUF(DID); ABC_CHECK_NULL_BUF(LPIN1); pJSON_Root = json_pack("{ss, ss}", ABC_SERVER_JSON_DID_FIELD, base64Encode(DID).c_str(), ABC_SERVER_JSON_LPIN1_FIELD, base64Encode(LPIN1).c_str()); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); // Check the result ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); // get the results field pJSON_Value = replyJson.results().get(); ABC_CHECK_ASSERT((pJSON_Value && json_is_object(pJSON_Value)), ABC_CC_JSONError, "Error parsing server JSON pin package results"); // get the pin_package field pJSON_Value = json_object_get(pJSON_Value, JSON_ACCT_PIN_PACKAGE); ABC_CHECK_ASSERT((pJSON_Value && json_is_string(pJSON_Value)), ABC_CC_JSONError, "Error pin package JSON results"); // copy the value ABC_STRDUP(*szPinPackage, json_string_value(pJSON_Value)); exit: if (pJSON_Root) json_decref(pJSON_Root); ABC_FREE_STR(szPost); return cc; }
/** * Creates an account on the server. * * This function sends information to the server to create an account. * If the account was created, ABC_CC_Ok is returned. * If the account already exists, ABC_CC_AccountAlreadyExists is returned. * * @param LP1 Password hash for the account */ tABC_CC ABC_LoginServerCreate(const Lobby &lobby, tABC_U08Buf LP1, const CarePackage &carePackage, const LoginPackage &loginPackage, const char *szRepoAcctKey, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_ACCOUNT_CREATE_PATH; ServerReplyJson replyJson; char *szPost = NULL; std::string carePackageStr; std::string loginPackageStr; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(LP1); ABC_CHECK_NEW(carePackage.encode(carePackageStr)); ABC_CHECK_NEW(loginPackage.encode(loginPackageStr)); // create the post data pJSON_Root = json_pack("{ssssssssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_CARE_PACKAGE_FIELD, carePackageStr.c_str(), ABC_SERVER_JSON_LOGIN_PACKAGE_FIELD, loginPackageStr.c_str(), ABC_SERVER_JSON_REPO_FIELD, szRepoAcctKey); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); // decode the result ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
tABC_CC ABC_LoginServerOtpRequest(const char *szUrl, const Lobby &lobby, tABC_U08Buf LP1, JsonPtr *results, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; ServerReplyJson replyJson; char *szPost = NULL; json_t *pJSON_Root = NULL; // create the post data pJSON_Root = json_pack("{ss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str()); // If there is a LP1 provided if (LP1.data()) { json_object_set_new(pJSON_Root, ABC_SERVER_JSON_LP1_FIELD, json_string(base64Encode(LP1).c_str())); } { auto key = lobby.otpKey(); if (key) json_object_set_new(pJSON_Root, ABC_SERVER_JSON_OTP_FIELD, json_string(key->totp().c_str())); } json_object_set_new(pJSON_Root, ABC_SERVER_JSON_OTP_RESET_AUTH, json_string(gOtpResetAuth.c_str())); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, szUrl, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); if (results) *results = replyJson.results(); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
tABC_CC ABC_BridgeWatcherLoop(Wallet &self, tABC_BitCoin_Event_Callback fAsyncCallback, void *pData, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher::block_height_callback heightCallback; Watcher::tx_callback txCallback; Watcher::tx_sent_callback sendCallback; Watcher::quiet_callback on_quiet; Watcher::fail_callback failCallback; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); watcherInfo->fAsyncCallback = fAsyncCallback; watcherInfo->pData = pData; txCallback = [watcherInfo, fAsyncCallback, pData] (const libbitcoin::transaction_type& tx) { ABC_BridgeTxCallback(watcherInfo, tx, fAsyncCallback, pData); }; watcherInfo->watcher.set_tx_callback(txCallback); heightCallback = [watcherInfo, fAsyncCallback, pData](const size_t height) { if (fAsyncCallback) { tABC_AsyncBitCoinInfo info; info.eventType = ABC_AsyncEventType_BlockHeightChange; info.pData = pData; info.szDescription = "Block height change"; fAsyncCallback(&info); } watcherSave(watcherInfo->wallet); // Failure is not fatal }; watcherInfo->watcher.set_height_callback(heightCallback); on_quiet = [watcherInfo]() { ABC_BridgeQuietCallback(watcherInfo); }; watcherInfo->watcher.set_quiet_callback(on_quiet); failCallback = [watcherInfo]() { tABC_Error error; ABC_BridgeWatcherConnect(watcherInfo->wallet, &error); }; watcherInfo->watcher.set_fail_callback(failCallback); watcherInfo->watcher.loop(); exit: return cc; }
/** * Enables 2 Factor authentication * * @param LP1 Password hash for the account * @param timeout Amount of time needed for a reset to complete */ tABC_CC ABC_LoginServerOtpEnable(const Lobby &lobby, tABC_U08Buf LP1, const char *szOtpSecret, const long timeout, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/otp/on"; ServerReplyJson replyJson; char *szPost = NULL; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(LP1); // create the post data pJSON_Root = json_pack("{sssssssi}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_OTP_SECRET_FIELD, szOtpSecret, ABC_SERVER_JSON_OTP_TIMEOUT, timeout); { auto key = lobby.otpKey(); if (key) json_object_set_new(pJSON_Root, ABC_SERVER_JSON_OTP_FIELD, json_string(key->totp().c_str())); } szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
/** * Sets the recycle status on an address as specified * * @param szAddress ID of the address * @param pError A pointer to the location to store the error if there is one */ static tABC_CC ABC_TxSetAddressRecycle(Wallet &self, const char *szAddress, bool bRecyclable, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; AutoCoreLock lock(gCoreMutex); Address address; ABC_CHECK_NEW(self.addresses.get(address, szAddress)); if (address.recyclable != bRecyclable) { address.recyclable = bRecyclable; ABC_CHECK_NEW(self.addresses.save(address)); } exit: return cc; }
/** * Creates a receive request. * * @param szUserName UserName for the account associated with this request * @param pDetails Pointer to transaction details * @param pszRequestID Pointer to store allocated ID for this request * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_TxCreateReceiveRequest(Wallet &self, tABC_TxDetails *pDetails, char **pszRequestID, bool bTransfer, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Address address; ABC_CHECK_NEW(self.addresses.getNew(address)); address.time = time(nullptr); address.metadata = pDetails; ABC_CHECK_NEW(self.addresses.save(address)); // set the id for the caller *pszRequestID = stringCopy(address.address); exit: return cc; }
tABC_CC ABC_BridgeWatcherDisconnect(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); watcher->disconnect(); exit: return cc; }
/** * Gets the bit coin public address for a specified request * * @param szRequestID ID of request * @param pszAddress Location to store allocated address string (caller must free) * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_TxGetRequestAddress(Wallet &self, const char *szRequestID, char **pszAddress, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Address address; ABC_CHECK_NEW(self.addresses.get(address, szRequestID)); *pszAddress = stringCopy(address.address); exit: return cc; }
static tABC_CC ABC_WalletServerRepoPost(const Lobby &lobby, tABC_U08Buf LP1, const char *szWalletAcctKey, const char *szPath, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; HttpReply reply; std::string url = ABC_SERVER_ROOT "/" + std::string(szPath); ServerReplyJson replyJson; char *szPost = NULL; json_t *pJSON_Root = NULL; ABC_CHECK_NULL_BUF(LP1); // create the post data pJSON_Root = json_pack("{ssssss}", ABC_SERVER_JSON_L1_FIELD, base64Encode(lobby.authId()).c_str(), ABC_SERVER_JSON_LP1_FIELD, base64Encode(LP1).c_str(), ABC_SERVER_JSON_REPO_WALLET_FIELD, szWalletAcctKey); szPost = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_COMPACT); // send the command ABC_CHECK_NEW(AirbitzRequest().post(reply, url, szPost)); ABC_CHECK_NEW(replyJson.decode(reply.body)); ABC_CHECK_NEW(replyJson.ok()); exit: ABC_FREE_STR(szPost); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
tABC_CC ABC_BridgeTxBlockHeight(Wallet &self, unsigned int *height, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); *height = watcher->get_last_block_height(); if (*height == 0) { cc = ABC_CC_Synchronizing; } exit: return cc; }
tABC_CC ABC_LoginServerGetCarePackage(const Lobby &lobby, CarePackage &result, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_GET_CARE_PACKAGE_PATH; char *szCarePackage = NULL; ABC_CHECK_RET(ABC_LoginServerGetString(lobby, U08Buf(), U08Buf(), url.c_str(), JSON_ACCT_CARE_PACKAGE, &szCarePackage, pError)); ABC_CHECK_NEW(result.decode(szCarePackage)); exit: ABC_FREE_STR(szCarePackage); return cc; }
static tABC_CC ABC_LoginPasswordServer(std::shared_ptr<Login> &result, std::shared_ptr<Lobby> lobby, tABC_U08Buf LP, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::unique_ptr<Login> login; tABC_CarePackage *pCarePackage = NULL; tABC_LoginPackage *pLoginPackage = NULL; tABC_U08Buf LRA1 = ABC_BUF_NULL; // Do not free AutoU08Buf LP1; AutoU08Buf LP2; AutoU08Buf MK; // Get the CarePackage: ABC_CHECK_RET(ABC_LoginServerGetCarePackage(toU08Buf(lobby->authId()), &pCarePackage, pError)); // Get the LoginPackage: ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP1, &LP1, pError)); ABC_CHECK_RET(ABC_LoginServerGetLoginPackage(toU08Buf(lobby->authId()), LP1, LRA1, &pLoginPackage, pError)); // Decrypt MK: ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP2, &LP2, pError)); ABC_CHECK_RET(ABC_CryptoDecryptJSONObject(pLoginPackage->EMK_LP2, LP2, &MK, pError)); // Decrypt SyncKey: login.reset(new Login(lobby, static_cast<U08Buf>(MK))); ABC_CHECK_NEW(login->init(pLoginPackage), pError); // Set up the on-disk login: ABC_CHECK_RET(ABC_LoginDirSavePackages(lobby->dir(), pCarePackage, pLoginPackage, pError)); // Assign the result: result.reset(login.release()); exit: ABC_CarePackageFree(pCarePackage); ABC_LoginPackageFree(pLoginPackage); return cc; }
tABC_CC ABC_BridgeTxHeight(Wallet &self, const char *szTxId, unsigned int *height, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; int height_; bc::hash_digest txid; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); if (!bc::decode_hash(txid, szTxId)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); if (!watcher->get_tx_height(txid, height_)) { cc = ABC_CC_Synchronizing; } *height = height_; exit: return cc; }
tABC_CC ABC_BridgeWatchAddr(Wallet &self, const char *pubAddress, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_DebugLog("Watching %s for %s", pubAddress, self.id().c_str()); bc::payment_address addr; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); if (!addr.set_encoded(pubAddress)) { cc = ABC_CC_Error; ABC_DebugLog("Invalid pubAddress %s\n", pubAddress); goto exit; } watcherInfo->addresses.insert(pubAddress); watcherInfo->watcher.watch_address(addr); exit: return cc; }
static tABC_CC ABC_BridgeDoSweep(WatcherInfo *watcherInfo, PendingSweep& sweep, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; char *szID = NULL; char *szAddress = NULL; uint64_t funds = 0; abcd::unsigned_transaction utx; bc::transaction_output_type output; abcd::key_table keys; std::string malTxId, txId; // Find utxos for this address: auto utxos = watcherInfo->watcher.get_utxos(sweep.address); // Bail out if there are no funds to sweep: if (!utxos.size()) { // Tell the GUI if there were funds in the past: if (watcherInfo->watcher.db().has_history(sweep.address)) { if (sweep.fCallback) { sweep.fCallback(ABC_CC_Ok, NULL, 0); } else { if (watcherInfo->fAsyncCallback) { tABC_AsyncBitCoinInfo info; info.eventType = ABC_AsyncEventType_IncomingSweep; info.sweepSatoshi = 0; info.szTxID = NULL; watcherInfo->fAsyncCallback(&info); } } sweep.done = true; } return ABC_CC_Ok; } // There are some utxos, so send them to ourselves: tABC_TxDetails details; memset(&details, 0, sizeof(tABC_TxDetails)); details.amountSatoshi = 0; details.amountCurrency = 0; details.amountFeesAirbitzSatoshi = 0; details.amountFeesMinersSatoshi = 0; details.szName = const_cast<char*>(""); details.szCategory = const_cast<char*>(""); details.szNotes = const_cast<char*>(""); details.attributes = 0x2; // Create a new receive request: ABC_CHECK_RET(ABC_TxCreateReceiveRequest(watcherInfo->wallet, &details, &szID, false, pError)); ABC_CHECK_RET(ABC_TxGetRequestAddress(watcherInfo->wallet, szID, &szAddress, pError)); // Build a transaction: utx.tx.version = 1; utx.tx.locktime = 0; for (auto &utxo : utxos) { bc::transaction_input_type input; input.sequence = 0xffffffff; input.previous_output = utxo.point; funds += utxo.value; utx.tx.inputs.push_back(input); } if (10000 < funds) funds -= 10000; // Ugh, hard-coded mining fee ABC_CHECK_ASSERT(!outputIsDust(funds), ABC_CC_InsufficientFunds, "Not enough funds"); output.value = funds; ABC_CHECK_NEW(outputScriptForAddress(output.script, szAddress)); utx.tx.outputs.push_back(output); // Now sign that: keys[sweep.address] = sweep.key; ABC_CHECK_SYS(abcd::gather_challenges(utx, watcherInfo->watcher), "gather_challenges"); ABC_CHECK_SYS(abcd::sign_tx(utx, keys), "sign_tx"); // Send: { bc::data_chunk raw_tx(satoshi_raw_size(utx.tx)); bc::satoshi_save(utx.tx, raw_tx.begin()); ABC_CHECK_NEW(broadcastTx(raw_tx)); } // Save the transaction in the database: malTxId = bc::encode_hash(bc::hash_transaction(utx.tx)); txId = ABC_BridgeNonMalleableTxId(utx.tx); ABC_CHECK_RET(ABC_TxSweepSaveTransaction(watcherInfo->wallet, txId.c_str(), malTxId.c_str(), funds, &details, pError)); // Done: if (sweep.fCallback) { sweep.fCallback(ABC_CC_Ok, txId.c_str(), output.value); } else { if (watcherInfo->fAsyncCallback) { tABC_AsyncBitCoinInfo info; info.eventType = ABC_AsyncEventType_IncomingSweep; info.sweepSatoshi = output.value; info.szTxID = stringCopy(txId); watcherInfo->fAsyncCallback(&info); ABC_FREE_STR(info.szTxID); } } sweep.done = true; watcherInfo->watcher.send_tx(utx.tx); exit: ABC_FREE_STR(szID); ABC_FREE_STR(szAddress); return cc; }
tABC_CC ABC_BridgeTxDetailsSplit(Wallet &self, const char *szTxID, tABC_TxOutput ***paInputs, unsigned int *pInCount, tABC_TxOutput ***paOutputs, unsigned int *pOutCount, int64_t *pAmount, int64_t *pFees, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_TxOutput **paInArr = NULL; tABC_TxOutput **paOutArr = NULL; bc::transaction_type tx; unsigned int idx = 0, iCount = 0, oCount = 0; int64_t fees = 0; int64_t totalInSatoshi = 0, totalOutSatoshi = 0, totalMeSatoshi = 0, totalMeInSatoshi = 0; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); bc::hash_digest txid; if (!bc::decode_hash(txid, szTxID)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); tx = watcherInfo->watcher.find_tx(txid); idx = 0; iCount = tx.inputs.size(); paInArr = (tABC_TxOutput **) malloc(sizeof(tABC_TxOutput *) * iCount); for (auto i : tx.inputs) { bc::payment_address addr; bc::extract(addr, i.script); auto prev = i.previous_output; // Create output tABC_TxOutput *out = (tABC_TxOutput *) malloc(sizeof(tABC_TxOutput)); out->input = true; out->szTxId = stringCopy(bc::encode_hash(prev.hash)); out->szAddress = stringCopy(addr.encoded()); auto tx = watcherInfo->watcher.find_tx(prev.hash); if (prev.index < tx.outputs.size()) { out->value = tx.outputs[prev.index].value; totalInSatoshi += tx.outputs[prev.index].value; auto row = watcherInfo->addresses.find(addr.encoded()); if (row != watcherInfo->addresses.end()) totalMeInSatoshi += tx.outputs[prev.index].value; } else { out->value = 0; } paInArr[idx] = out; idx++; } idx = 0; oCount = tx.outputs.size(); paOutArr = (tABC_TxOutput **) malloc(sizeof(tABC_TxOutput *) * oCount); for (auto o : tx.outputs) { bc::payment_address addr; bc::extract(addr, o.script); // Create output tABC_TxOutput *out = (tABC_TxOutput *) malloc(sizeof(tABC_TxOutput)); out->input = false; out->value = o.value; out->szAddress = stringCopy(addr.encoded()); out->szTxId = stringCopy(szTxID); // Do we own this address? auto row = watcherInfo->addresses.find(addr.encoded()); if (row != watcherInfo->addresses.end()) { totalMeSatoshi += o.value; } totalOutSatoshi += o.value; paOutArr[idx] = out; idx++; } fees = totalInSatoshi - totalOutSatoshi; totalMeSatoshi -= totalMeInSatoshi; *paInputs = paInArr; *pInCount = iCount; *paOutputs = paOutArr; *pOutCount = oCount; *pAmount = totalMeSatoshi; *pFees = fees; paInArr = NULL; paOutArr = NULL; exit: ABC_TxFreeOutputs(paInArr, iCount); ABC_TxFreeOutputs(paOutArr, oCount); return cc; }
/** * Generate the QR code for a previously created receive request. * * @param szRequestID ID of this request * @param pszURI Pointer to string to store URI(optional) * @param paData Pointer to store array of data bytes (0x0 white, 0x1 black) * @param pWidth Pointer to store width of image (image will be square) * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_TxGenerateRequestQRCode(Wallet &self, const char *szRequestID, char **pszURI, unsigned char **paData, unsigned int *pWidth, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; AutoCoreLock lock(gCoreMutex); QRcode *qr = NULL; unsigned char *aData = NULL; unsigned int length = 0; char *szURI = NULL; // load the request/address Address address; ABC_CHECK_NEW(self.addresses.get(address, szRequestID)); // Get the URL string for this info tABC_BitcoinURIInfo infoURI; memset(&infoURI, 0, sizeof(tABC_BitcoinURIInfo)); infoURI.amountSatoshi = address.metadata.amountSatoshi; infoURI.szAddress = address.address.c_str(); // Set the label if there is one ABC_CHECK_RET(ABC_TxBuildFromLabel(self, &(infoURI.szLabel), pError)); // if there is a note if (!address.metadata.notes.empty()) { infoURI.szMessage = address.metadata.notes.c_str(); } ABC_CHECK_RET(ABC_BridgeEncodeBitcoinURI(&szURI, &infoURI, pError)); // encode our string ABC_DebugLog("Encoding: %s", szURI); qr = QRcode_encodeString(szURI, 0, QR_ECLEVEL_L, QR_MODE_8, 1); ABC_CHECK_ASSERT(qr != NULL, ABC_CC_Error, "Unable to create QR code"); length = qr->width * qr->width; ABC_ARRAY_NEW(aData, length, unsigned char); for (unsigned i = 0; i < length; i++) { aData[i] = qr->data[i] & 0x1; } *pWidth = qr->width; *paData = aData; aData = NULL; if (pszURI != NULL) { *pszURI = stringCopy(szURI); } exit: ABC_FREE_STR(szURI); QRcode_free(qr); ABC_CLEAR_FREE(aData, length); return cc; }