/** * Sets the currency number of a wallet */ static tABC_CC ABC_WalletSetCurrencyNum(tABC_WalletID self, int currencyNum, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tWalletData *pData = NULL; char *szFilename = NULL; char *szJSON = NULL; // load the wallet data into the cache ABC_CHECK_RET(ABC_WalletCacheData(self, &pData, pError)); // set the currency number pData->currencyNum = currencyNum; // create the json for the currency number ABC_CHECK_RET(ABC_UtilCreateIntJSONString(currencyNum, JSON_WALLET_CURRENCY_NUM_FIELD, &szJSON, pError)); //printf("currency num:\n%s\n", szJSON); // write the name out to the file ABC_STR_NEW(szFilename, ABC_FILEIO_MAX_PATH_LENGTH); sprintf(szFilename, "%s/%s", pData->szWalletSyncDir, WALLET_CURRENCY_FILENAME); tABC_U08Buf Data; ABC_BUF_SET_PTR(Data, (unsigned char *)szJSON, strlen(szJSON) + 1); ABC_CHECK_RET(ABC_CryptoEncryptJSONFile(Data, pData->MK, ABC_CryptoType_AES256, szFilename, pError)); exit: ABC_FREE_STR(szFilename); ABC_FREE_STR(szJSON); 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; }
/** * Validates that the provided password is correct. * This is used in the GUI to guard access to certain actions. * * @param szPassword The password to check. * @param pOk Set to true if the password is good, or false otherwise. */ tABC_CC ABC_LoginPasswordOk(Login &login, const char *szPassword, bool *pOk, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_CarePackage *pCarePackage = NULL; tABC_LoginPackage *pLoginPackage = NULL; AutoU08Buf LP; AutoU08Buf LP2; AutoU08Buf MK; *pOk = false; // Load the packages: ABC_CHECK_RET(ABC_LoginDirLoadPackages(login.lobby().dir(), &pCarePackage, &pLoginPackage, pError)); // LP = L + P: ABC_BUF_STRCAT(LP, login.lobby().username().c_str(), szPassword); ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP2, &LP2, pError)); // Try to decrypt MK: tABC_Error error; if (ABC_CC_Ok == ABC_CryptoDecryptJSONObject(pLoginPackage->EMK_LP2, LP2, &MK, &error)) { *pOk = true; } exit: ABC_CarePackageFree(pCarePackage); ABC_LoginPackageFree(pLoginPackage); return cc; }
/** * Sets the name of a wallet */ tABC_CC ABC_WalletSetName(tABC_WalletID self, const char *szName, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tWalletData *pData = NULL; char *szFilename = NULL; char *szJSON = NULL; // load the wallet data into the cache ABC_CHECK_RET(ABC_WalletCacheData(self, &pData, pError)); // set the new name ABC_FREE_STR(pData->szName); ABC_STRDUP(pData->szName, szName); // create the json for the wallet name ABC_CHECK_RET(ABC_UtilCreateValueJSONString(szName, JSON_WALLET_NAME_FIELD, &szJSON, pError)); //printf("name:\n%s\n", szJSON); // write the name out to the file ABC_STR_NEW(szFilename, ABC_FILEIO_MAX_PATH_LENGTH); sprintf(szFilename, "%s/%s", pData->szWalletSyncDir, WALLET_NAME_FILENAME); tABC_U08Buf Data; ABC_BUF_SET_PTR(Data, (unsigned char *)szJSON, strlen(szJSON) + 1); ABC_CHECK_RET(ABC_CryptoEncryptJSONFile(Data, pData->MK, ABC_CryptoType_AES256, szFilename, pError)); exit: ABC_FREE_STR(szFilename); ABC_FREE_STR(szJSON); return cc; }
tABC_CC ABC_WalletSyncAll(tABC_SyncKeys *pKeys, int *pDirty, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_SET_ERR_CODE(pError, ABC_CC_Ok); char **aszUUIDs = NULL; unsigned int i = 0; unsigned int nUUIDs = 0; // Its not dirty...yet *pDirty = 0; // Get the wallet list ABC_CHECK_RET(ABC_AccountWalletList(pKeys, &aszUUIDs, &nUUIDs, pError)); for (i = 0; i < nUUIDs; ++i) { char *szUUID = aszUUIDs[i]; int dirty = 0; ABC_CHECK_RET(ABC_WalletSyncData(ABC_WalletID(pKeys, szUUID), &dirty, pError)); if (dirty) { *pDirty = 1; } } exit: ABC_UtilFreeStringArray(aszUUIDs, nUUIDs); return cc; }
/** * Changes the password on an existing login object. * @param szPassword The new password. */ tABC_CC ABC_LoginPasswordSet(Login &login, const char *szPassword, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_CarePackage *pCarePackage = NULL; tABC_LoginPackage *pLoginPackage = NULL; AutoU08Buf oldL1; AutoU08Buf oldLP1; AutoU08Buf oldLRA1; AutoU08Buf LP; AutoU08Buf LP1; AutoU08Buf LP2; // Load the packages: ABC_CHECK_RET(ABC_LoginDirLoadPackages(login.lobby().dir(), &pCarePackage, &pLoginPackage, pError)); // Load the old keys: ABC_CHECK_RET(ABC_LoginGetServerKeys(login, &oldL1, &oldLP1, pError)); if (pLoginPackage->ELRA1) { ABC_CHECK_RET(ABC_CryptoDecryptJSONObject(pLoginPackage->ELRA1, toU08Buf(login.dataKey()), &oldLRA1, pError)); } // Update SNRP2: ABC_CryptoFreeSNRP(pCarePackage->pSNRP2); pCarePackage->pSNRP2 = nullptr; ABC_CHECK_RET(ABC_CryptoCreateSNRPForClient(&pCarePackage->pSNRP2, pError)); // LP = L + P: ABC_BUF_STRCAT(LP, login.lobby().username().c_str(), szPassword); // Update EMK_LP2: json_decref(pLoginPackage->EMK_LP2); ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP2, &LP2, pError)); ABC_CHECK_RET(ABC_CryptoEncryptJSONObject(toU08Buf(login.dataKey()), LP2, ABC_CryptoType_AES256, &pLoginPackage->EMK_LP2, pError)); // Update ELP1: json_decref(pLoginPackage->ELP1); ABC_CHECK_RET(ABC_CryptoScryptSNRP(LP, pCarePackage->pSNRP1, &LP1, pError)); ABC_CHECK_RET(ABC_CryptoEncryptJSONObject(LP1, toU08Buf(login.dataKey()), ABC_CryptoType_AES256, &pLoginPackage->ELP1, pError)); // Change the server login: ABC_CHECK_RET(ABC_LoginServerChangePassword(oldL1, oldLP1, LP1, oldLRA1, pCarePackage, pLoginPackage, pError)); // Change the on-disk login: ABC_CHECK_RET(ABC_LoginDirSavePackages(login.lobby().dir(), pCarePackage, pLoginPackage, pError)); exit: ABC_CarePackageFree(pCarePackage); ABC_LoginPackageFree(pLoginPackage); return cc; }
tABC_CC ABC_LoginServerOtpStatus(const Lobby &lobby, tABC_U08Buf LP1, bool *on, long *timeout, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; json_t *pJSON_Value = NULL; JsonPtr reply; std::string url = ABC_SERVER_ROOT "/otp/status"; ABC_CHECK_RET(ABC_LoginServerOtpRequest(url.c_str(), lobby, LP1, &reply, pError)); pJSON_Value = json_object_get(reply.get(), ABC_SERVER_JSON_OTP_ON); ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error otp/on JSON"); *on = json_is_true(pJSON_Value); if (*on) { pJSON_Value = json_object_get(reply.get(), ABC_SERVER_JSON_OTP_TIMEOUT); ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error otp/timeout JSON"); *timeout = json_integer_value(pJSON_Value); } exit: return cc; }
/** * Decrypts and hex-encodes the SyncKey stored in the login package. */ tABC_CC ABC_LoginPackageGetSyncKey(tABC_LoginPackage *pSelf, const tABC_U08Buf MK, char **pszSyncKey, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_U08Buf SyncKey = ABC_BUF_NULL; ABC_CHECK_RET(ABC_CryptoDecryptJSONObject(pSelf->ESyncKey, MK, &SyncKey, pError)); ABC_CHECK_RET(ABC_CryptoHexEncode(SyncKey, pszSyncKey, pError)); exit: ABC_BUF_FREE(SyncKey); return cc; }
/** * Loads a CarePackage object from a string. */ tABC_CC ABC_CarePackageDecode(tABC_CarePackage **ppSelf, char *szCarePackage, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_CarePackage *pSelf = NULL; json_t *pJSON_Root = NULL; json_t *pJSON_SNRP2 = NULL; json_t *pJSON_SNRP3 = NULL; json_t *pJSON_SNRP4 = NULL; int e; // Allocate self: ABC_NEW(pSelf, tABC_CarePackage); // Parse the JSON: json_error_t error; pJSON_Root = json_loads(szCarePackage, 0, &error); ABC_CHECK_ASSERT(pJSON_Root != NULL, ABC_CC_JSONError, "Error parsing CarePackage JSON"); ABC_CHECK_ASSERT(json_is_object(pJSON_Root), ABC_CC_JSONError, "Error parsing CarePackage JSON"); // Unpack the contents: e = json_unpack(pJSON_Root, "{s:o, s:o, s:o, s?o}", JSON_ACCT_SNRP2_FIELD, &pJSON_SNRP2, JSON_ACCT_SNRP3_FIELD, &pJSON_SNRP3, JSON_ACCT_SNRP4_FIELD, &pJSON_SNRP4, JSON_ACCT_ERQ_FIELD, &pSelf->ERQ); ABC_CHECK_SYS(!e, "Error parsing CarePackage JSON"); // Decode SNRP's: ABC_CHECK_RET(ABC_CryptoCreateSNRPForServer(&pSelf->pSNRP1, pError)); ABC_CHECK_RET(ABC_CryptoDecodeJSONObjectSNRP(pJSON_SNRP2, &pSelf->pSNRP2, pError)); ABC_CHECK_RET(ABC_CryptoDecodeJSONObjectSNRP(pJSON_SNRP3, &pSelf->pSNRP3, pError)); ABC_CHECK_RET(ABC_CryptoDecodeJSONObjectSNRP(pJSON_SNRP4, &pSelf->pSNRP4, pError)); // Save everything: if (pSelf->ERQ) json_incref(pSelf->ERQ); *ppSelf = pSelf; pSelf = NULL; exit: if (pSelf) ABC_CarePackageFree(pSelf); if (pJSON_Root) json_decref(pJSON_Root); return cc; }
/** * Request Reset 2 Factor authentication * * @param LP1 Password hash for the account */ tABC_CC ABC_LoginServerOtpResetCancelPending(const Lobby &lobby, tABC_U08Buf LP1, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::string url = ABC_SERVER_ROOT "/otp/pending/cancel"; ABC_CHECK_RET(ABC_LoginServerOtpRequest(url.c_str(), lobby, LP1, NULL, pError)); exit: return cc; }
/** * Request Reset 2 Factor authentication * * @param LP1 Password hash for the account */ tABC_CC ABC_LoginServerOtpReset(const Lobby &lobby, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::string url = ABC_SERVER_ROOT "/otp/reset"; ABC_CHECK_RET(ABC_LoginServerOtpRequest(url.c_str(), lobby, U08Buf(), NULL, pError)); exit: return cc; }
/** * Load the general info. * * This function will load the general info which includes information on * Obelisk Servers, AirBitz fees and miners fees. */ tABC_CC ABC_GeneralGetInfo(tABC_GeneralInfo **ppInfo, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; json_t *pJSON_Root = NULL; json_t *pJSON_Value = NULL; char *szInfoFilename = NULL; tABC_GeneralInfo *pInfo = NULL; json_t *pJSON_MinersFeesArray = NULL; json_t *pJSON_AirBitzFees = NULL; json_t *pJSON_ObeliskArray = NULL; json_t *pJSON_SyncArray = NULL; bool bExists = false; ABC_CHECK_NULL(ppInfo); // get the info filename ABC_CHECK_RET(ABC_GeneralGetInfoFilename(&szInfoFilename, pError)); // check to see if we have the file ABC_CHECK_RET(ABC_FileIOFileExists(szInfoFilename, &bExists, pError)); if (false == bExists) { // pull it down from the server ABC_CHECK_RET(ABC_GeneralUpdateInfo(pError)); } // load the json ABC_CHECK_RET(ABC_FileIOReadFileObject(szInfoFilename, &pJSON_Root, true, pError)); // allocate the struct ABC_NEW(pInfo, tABC_GeneralInfo); // get the miners fees array pJSON_MinersFeesArray = json_object_get(pJSON_Root, JSON_INFO_MINERS_FEES_FIELD); ABC_CHECK_ASSERT((pJSON_MinersFeesArray && json_is_array(pJSON_MinersFeesArray)), ABC_CC_JSONError, "Error parsing JSON array value"); // get the number of elements in the array pInfo->countMinersFees = (unsigned int) json_array_size(pJSON_MinersFeesArray); if (pInfo->countMinersFees > 0) { ABC_ARRAY_NEW(pInfo->aMinersFees, pInfo->countMinersFees, tABC_GeneralMinerFee*); }
/** * Copies the strings from one tABC_WalletID struct to another. */ tABC_CC ABC_WalletIDCopy(tABC_WalletID *out, tABC_WalletID in, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_CHECK_RET(ABC_SyncKeysCopy(&out->pKeys, in.pKeys, pError)); ABC_STRDUP(out->szUUID, in.szUUID); exit: return cc; }
/** * Serializes a CarePackage object to a JSON string. */ tABC_CC ABC_CarePackageEncode(tABC_CarePackage *pSelf, char **pszCarePackage, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; json_t *pJSON_Root = NULL; json_t *pJSON_SNRP2 = NULL; json_t *pJSON_SNRP3 = NULL; json_t *pJSON_SNRP4 = NULL; // Build the SNRP's: ABC_CHECK_RET(ABC_CryptoCreateJSONObjectSNRP(pSelf->pSNRP2, &pJSON_SNRP2, pError)); ABC_CHECK_RET(ABC_CryptoCreateJSONObjectSNRP(pSelf->pSNRP3, &pJSON_SNRP3, pError)); ABC_CHECK_RET(ABC_CryptoCreateJSONObjectSNRP(pSelf->pSNRP4, &pJSON_SNRP4, pError)); // Build the main body: pJSON_Root = json_pack("{s:O, s:O, s:O}", JSON_ACCT_SNRP2_FIELD, pJSON_SNRP2, JSON_ACCT_SNRP3_FIELD, pJSON_SNRP3, JSON_ACCT_SNRP4_FIELD, pJSON_SNRP4); ABC_CHECK_NULL(pJSON_Root); // Build the ERQ, if any: if (pSelf->ERQ) { ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_ERQ_FIELD, pSelf->ERQ)); } // Write out: *pszCarePackage = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_INDENT(4) | JSON_PRESERVE_ORDER); ABC_CHECK_NULL(*pszCarePackage); exit: if (pJSON_Root) json_decref(pJSON_Root); if (pJSON_SNRP2) json_decref(pJSON_SNRP2); if (pJSON_SNRP3) json_decref(pJSON_SNRP3); if (pJSON_SNRP4) json_decref(pJSON_SNRP4); return cc; }
/** * Cancels a previously created receive request. * This is done by setting the recycle bit to true so that the address can be used again. * * @param szRequestID ID of this request * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_TxCancelReceiveRequest(Wallet &self, const char *szRequestID, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_SET_ERR_CODE(pError, ABC_CC_Ok); // set the recycle bool to true (not that the request is actually an address internally) ABC_CHECK_RET(ABC_TxSetAddressRecycle(self, szRequestID, true, pError)); exit: return cc; }
/** * Constructs a fresh CarePackage object with default values for all fields. */ tABC_CC ABC_CarePackageNew(tABC_CarePackage **ppSelf, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_CarePackage *pSelf = NULL; // Allocate self: ABC_NEW(pSelf, tABC_CarePackage); // Generate SNRP's: ABC_CHECK_RET(ABC_CryptoCreateSNRPForServer(&pSelf->pSNRP1, pError)); ABC_CHECK_RET(ABC_CryptoCreateSNRPForClient(&pSelf->pSNRP2, pError)); ABC_CHECK_RET(ABC_CryptoCreateSNRPForClient(&pSelf->pSNRP3, pError)); ABC_CHECK_RET(ABC_CryptoCreateSNRPForClient(&pSelf->pSNRP4, pError)); *ppSelf = pSelf; pSelf = NULL; exit: if (pSelf) ABC_CarePackageFree(pSelf); return cc; }
tABC_CC ABC_TxWatchAddresses(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; AutoCoreLock lock(gCoreMutex); auto addresses = self.addresses.list(); for (const auto &i: addresses) { ABC_CHECK_RET(ABC_BridgeWatchAddr(self, i.c_str(), pError)); } 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; }
/** * Adds the given account to the list of accounts that uses this wallet */ static tABC_CC ABC_WalletAddAccount(tABC_WalletID self, const char *szAccount, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tWalletData *pData = NULL; char *szFilename = NULL; json_t *dataJSON = NULL; // load the wallet data into the cache ABC_CHECK_RET(ABC_WalletCacheData(self, &pData, pError)); // if there are already accounts in the list if ((pData->aszAccounts) && (pData->numAccounts > 0)) { ABC_ARRAY_RESIZE(pData->aszAccounts, pData->numAccounts + 1, char*); }
/** * Upload files to auth server for debugging * * @param szUserName UserName for the account associated with the settings * @param szPassword Password for the account associated with the settings * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_LoginServerUploadLogs(const Account &account, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_SET_ERR_CODE(pError, ABC_CC_Ok); HttpReply reply; std::string url = ABC_SERVER_ROOT "/" ABC_SERVER_DEBUG_PATH; char *szPost = NULL; char *szLogFilename = NULL; json_t *pJSON_Root = NULL; DataChunk logData; DataChunk watchData; auto ids = account.wallets.list(); json_t *pJSON_array = NULL; AutoU08Buf LP1; ABC_CHECK_RET(ABC_LoginGetServerKey(account.login, &LP1, pError)); ABC_CHECK_RET(ABC_DebugLogFilename(&szLogFilename, pError);)
/** * Loads an existing login object, either from the server or from disk. * * @param szPassword The password for the account. */ tABC_CC ABC_LoginPassword(std::shared_ptr<Login> &result, std::shared_ptr<Lobby> lobby, const char *szPassword, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_Error error; // LP = L + P: AutoU08Buf LP; ABC_BUF_STRCAT(LP, lobby->username().c_str(), szPassword); // Try the login both ways: cc = ABC_LoginPasswordDisk(result, lobby, LP, &error); if (ABC_CC_Ok != cc) { ABC_CHECK_RET(ABC_LoginPasswordServer(result, lobby, LP, pError)); } exit: return cc; }
/** * Create a label based off the user settings * * @param pszLabel The label will be returned in this parameter * @param pError A pointer to the location to store the error if there is one */ static tABC_CC ABC_TxBuildFromLabel(Wallet &self, const char **pszLabel, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_SET_ERR_CODE(pError, ABC_CC_Ok); AutoFree<tABC_AccountSettings, ABC_FreeAccountSettings> pSettings; ABC_CHECK_NULL(pszLabel); *pszLabel = NULL; ABC_CHECK_RET(ABC_AccountSettingsLoad(self.account, &pSettings.get(), pError)); if (pSettings->bNameOnPayments && pSettings->szFullName) { *pszLabel = stringCopy(pSettings->szFullName); } exit: return cc; }
tABC_CC ABC_BridgeTxDetails(Wallet &self, const char *szTxID, tABC_TxOutput ***paOutputs, unsigned int *pCount, int64_t *pAmount, int64_t *pFees, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_TxOutput **paInArr = NULL; tABC_TxOutput **paOutArr = NULL; tABC_TxOutput **farr = NULL; unsigned int outCount = 0; unsigned int inCount = 0; unsigned int totalCount = 0; ABC_CHECK_RET(ABC_BridgeTxDetailsSplit(self, szTxID, &paInArr, &inCount, &paOutArr, &outCount, pAmount, pFees, pError)); farr = (tABC_TxOutput **) malloc(sizeof(tABC_TxOutput *) * (inCount + outCount)); totalCount = outCount + inCount; for (unsigned i = 0; i < totalCount; ++i) { if (i < inCount) { farr[i] = paInArr[i]; paInArr[i] = NULL; } else { farr[i] = paOutArr[i - inCount]; paOutArr[i - inCount] = NULL; } } *paOutputs = farr; *pCount = totalCount; farr = NULL; exit: ABC_TxFreeOutputs(farr, inCount + outCount); ABC_TxFreeOutputs(paInArr, inCount); ABC_TxFreeOutputs(paOutArr, outCount); 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; }
/** * Sync the wallet's data */ tABC_CC ABC_WalletSyncData(tABC_WalletID self, int *pDirty, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_SET_ERR_CODE(pError, ABC_CC_Ok); tABC_GeneralInfo *pInfo = NULL; char *szDirectory = NULL; char *szSyncDirectory = NULL; tWalletData *pData = NULL; bool bExists = false; bool bNew = false; // Fetch general info ABC_CHECK_RET(ABC_GeneralGetInfo(&pInfo, pError)); // create the wallet root directory if necessary ABC_CHECK_RET(ABC_WalletCreateRootDir(pError)); // create the wallet directory - <Wallet_UUID1> <- All data in this directory encrypted with MK_<Wallet_UUID1> ABC_CHECK_RET(ABC_WalletGetDirName(&szDirectory, self.szUUID, pError)); ABC_CHECK_RET(ABC_FileIOFileExists(szDirectory, &bExists, pError)); if (!bExists) { ABC_CHECK_RET(ABC_FileIOCreateDir(szDirectory, pError)); } // create the wallet sync dir under the main dir ABC_CHECK_RET(ABC_WalletGetSyncDirName(&szSyncDirectory, self.szUUID, pError)); ABC_CHECK_RET(ABC_FileIOFileExists(szSyncDirectory, &bExists, pError)); if (!bExists) { ABC_CHECK_RET(ABC_FileIOCreateDir(szSyncDirectory, pError)); // Init repo ABC_CHECK_RET(ABC_SyncMakeRepo(szSyncDirectory, pError)); bNew = true; } // load the wallet data into the cache ABC_CHECK_RET(ABC_WalletCacheData(self, &pData, pError)); ABC_CHECK_ASSERT(NULL != pData->szWalletAcctKey, ABC_CC_Error, "Expected to find RepoAcctKey in key cache"); // Sync ABC_CHECK_RET(ABC_SyncRepo(pData->szWalletSyncDir, pData->szWalletAcctKey, pDirty, pError)); if (*pDirty || bNew) { *pDirty = 1; ABC_CHECK_RET(ABC_WalletClearCache(pError)); } exit: ABC_FREE_STR(szSyncDirectory); ABC_FREE_STR(szDirectory); ABC_GeneralFreeInfo(pInfo); return cc; }
/** * Loads the settings for a specific account using the given key * If no settings file exists for the given user, defaults are created * * @param login Access to the account sync dir * @param ppSettings Location to store ptr to allocated settings (caller must free) * @param pError A pointer to the location to store the error if there is one */ tABC_CC ABC_AccountSettingsLoad(const Login &login, tABC_AccountSettings **ppSettings, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; AutoCoreLock lock(gCoreMutex); tABC_AccountSettings *pSettings = NULL; json_t *pJSON_Root = NULL; json_t *pJSON_Value = NULL; bool bExists = false; auto filename = login.syncDir() + ACCOUNT_SETTINGS_FILENAME; ABC_CHECK_NULL(ppSettings); ABC_CHECK_RET(ABC_FileIOFileExists(filename.c_str(), &bExists, pError)); if (true == bExists) { // load and decrypted the file into a json object ABC_CHECK_RET(ABC_CryptoDecryptJSONFileObject(filename.c_str(), toU08Buf(login.dataKey()), &pJSON_Root, pError)); //ABC_DebugLog("Loaded settings JSON:\n%s\n", json_dumps(pJSON_Root, JSON_INDENT(4) | JSON_PRESERVE_ORDER)); // allocate the new settings object ABC_NEW(pSettings, tABC_AccountSettings); pSettings->szFirstName = NULL; pSettings->szLastName = NULL; pSettings->szNickname = NULL; // get the first name pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_FIRST_NAME_FIELD); if (pJSON_Value) { ABC_CHECK_ASSERT(json_is_string(pJSON_Value), ABC_CC_JSONError, "Error parsing JSON string value"); ABC_STRDUP(pSettings->szFirstName, json_string_value(pJSON_Value)); } // get the last name pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_LAST_NAME_FIELD); if (pJSON_Value) { ABC_CHECK_ASSERT(json_is_string(pJSON_Value), ABC_CC_JSONError, "Error parsing JSON string value"); ABC_STRDUP(pSettings->szLastName, json_string_value(pJSON_Value)); } // get the nickname pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_NICKNAME_FIELD); if (pJSON_Value) { ABC_CHECK_ASSERT(json_is_string(pJSON_Value), ABC_CC_JSONError, "Error parsing JSON string value"); ABC_STRDUP(pSettings->szNickname, json_string_value(pJSON_Value)); } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_PIN_FIELD); if (pJSON_Value) { ABC_CHECK_ASSERT(json_is_string(pJSON_Value), ABC_CC_JSONError, "Error parsing JSON string value"); ABC_STRDUP(pSettings->szPIN, json_string_value(pJSON_Value)); } // get name on payments option pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_NAME_ON_PAYMENTS_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON boolean value"); pSettings->bNameOnPayments = json_is_true(pJSON_Value) ? true : false; // get minutes auto logout pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_MINUTES_AUTO_LOGOUT_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON integer value"); pSettings->minutesAutoLogout = (int) json_integer_value(pJSON_Value); pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_RECOVERY_REMINDER_COUNT); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON integer value"); pSettings->recoveryReminderCount = (int) json_integer_value(pJSON_Value); } else { pSettings->recoveryReminderCount = 0; } // get language pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_LANGUAGE_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_string(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON string value"); ABC_STRDUP(pSettings->szLanguage, json_string_value(pJSON_Value)); // get currency num pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_NUM_CURRENCY_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON integer value"); pSettings->currencyNum = (int) json_integer_value(pJSON_Value); // get advanced features pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_ADVANCED_FEATURES_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON boolean value"); pSettings->bAdvancedFeatures = json_is_true(pJSON_Value) ? true : false; pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_DAILY_SPEND_LIMIT_ENABLED); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON boolean value"); pSettings->bDailySpendLimit = json_is_true(pJSON_Value) ? true : false; } else { pSettings->bDailySpendLimit = false; } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_DAILY_SPEND_LIMIT_SATOSHIS); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON daily spend satoshi value"); pSettings->dailySpendLimitSatoshis = (int64_t) json_integer_value(pJSON_Value); } else { pSettings->dailySpendLimitSatoshis = 0; } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_SPEND_REQUIRE_PIN_ENABLED); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON boolean value"); pSettings->bSpendRequirePin = json_is_true(pJSON_Value) ? true : false; } else { // Default to PIN required pSettings->bSpendRequirePin = true; } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_DISABLE_PIN_LOGIN); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_boolean(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON boolean value"); pSettings->bDisablePINLogin = json_is_true(pJSON_Value) ? true : false; } else { // Default to PIN login allowed pSettings->bDisablePINLogin = false; } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_PIN_LOGIN_COUNT); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON pin login count"); pSettings->pinLoginCount = json_integer_value(pJSON_Value); } else { // Default to PIN login allowed pSettings->pinLoginCount = 0; } pJSON_Value = json_object_get(pJSON_Root, JSON_ACCT_SPEND_REQUIRE_PIN_SATOSHIS); if (pJSON_Value) { ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON daily spend satoshi value"); pSettings->spendRequirePinSatoshis = (int64_t) json_integer_value(pJSON_Value); } else { // Default PIN requirement to 50mb pSettings->spendRequirePinSatoshis = DEF_REQUIRE_PIN_SATOSHIS; } // get the denomination object json_t *pJSON_Denom = json_object_get(pJSON_Root, JSON_ACCT_BITCOIN_DENOMINATION_FIELD); ABC_CHECK_ASSERT((pJSON_Denom && json_is_object(pJSON_Denom)), ABC_CC_JSONError, "Error parsing JSON object value"); // get denomination satoshi display size (e.g., 100,000 would be milli-bit coin) pJSON_Value = json_object_get(pJSON_Denom, JSON_ACCT_SATOSHI_FIELD); ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON integer value"); pSettings->bitcoinDenomination.satoshi = json_integer_value(pJSON_Value); // get denomination type pJSON_Value = json_object_get(pJSON_Denom, JSON_ACCT_LABEL_TYPE); ABC_CHECK_ASSERT((pJSON_Value && json_is_integer(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON integer value"); pSettings->bitcoinDenomination.denominationType = json_integer_value(pJSON_Value); // get the exchange rates array json_t *pJSON_Sources = json_object_get(pJSON_Root, JSON_ACCT_EX_RATE_SOURCES_FIELD); ABC_CHECK_ASSERT((pJSON_Sources && json_is_array(pJSON_Sources)), ABC_CC_JSONError, "Error parsing JSON array value"); // get the number of elements in the array pSettings->exchangeRateSources.numSources = (int) json_array_size(pJSON_Sources); if (pSettings->exchangeRateSources.numSources > 0) { ABC_ARRAY_NEW(pSettings->exchangeRateSources.aSources, pSettings->exchangeRateSources.numSources, tABC_ExchangeRateSource*); }
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; }
/** * Creates the wallet with the given info. * * @param pszUUID Pointer to hold allocated pointer to UUID string */ tABC_CC ABC_WalletCreate(tABC_SyncKeys *pKeys, tABC_U08Buf L1, tABC_U08Buf LP1, const char *szUserName, const char *szWalletName, int currencyNum, unsigned int attributes, char **pszUUID, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; char *szFilename = NULL; char *szJSON = NULL; char *szUUID = NULL; char *szWalletDir = NULL; json_t *pJSON_Data = NULL; json_t *pJSON_Wallets = NULL; tABC_U08Buf WalletAcctKey = ABC_BUF_NULL; tWalletData *pData = NULL; ABC_CHECK_NULL(pszUUID); // create a new wallet data struct ABC_NEW(pData, tWalletData); pData->archived = 0; // create wallet guid ABC_CHECK_RET(ABC_CryptoGenUUIDString(&szUUID, pError)); ABC_STRDUP(pData->szUUID, szUUID); ABC_STRDUP(*pszUUID, szUUID); // generate the master key for this wallet - MK_<Wallet_GUID1> ABC_CHECK_RET(ABC_CryptoCreateRandomData(WALLET_KEY_LENGTH, &pData->MK, pError)); // create and set the bitcoin private seed for this wallet ABC_CHECK_RET(ABC_CryptoCreateRandomData(WALLET_BITCOIN_PRIVATE_SEED_LENGTH, &pData->BitcoinPrivateSeed, pError)); // Create Wallet Repo key ABC_CHECK_RET(ABC_CryptoCreateRandomData(SYNC_KEY_LENGTH, &WalletAcctKey, pError)); ABC_CHECK_RET(ABC_CryptoHexEncode(WalletAcctKey, &(pData->szWalletAcctKey), pError)); // create the wallet root directory if necessary ABC_CHECK_RET(ABC_WalletCreateRootDir(pError)); // create the wallet directory - <Wallet_UUID1> <- All data in this directory encrypted with MK_<Wallet_UUID1> ABC_CHECK_RET(ABC_WalletGetDirName(&(pData->szWalletDir), pData->szUUID, pError)); ABC_CHECK_RET(ABC_FileIOCreateDir(pData->szWalletDir, pError)); ABC_STRDUP(szWalletDir, pData->szWalletDir); // create the wallet sync dir under the main dir ABC_CHECK_RET(ABC_WalletGetSyncDirName(&(pData->szWalletSyncDir), pData->szUUID, pError)); ABC_CHECK_RET(ABC_FileIOCreateDir(pData->szWalletSyncDir, pError)); // we now have a new wallet so go ahead and cache its data ABC_CHECK_RET(ABC_WalletAddToCache(pData, pError)); // all the functions below assume the wallet is in the cache or can be loaded into the cache // set the wallet name ABC_CHECK_RET(ABC_WalletSetName(ABC_WalletID(pKeys, szUUID), szWalletName, pError)); // set the currency ABC_CHECK_RET(ABC_WalletSetCurrencyNum(ABC_WalletID(pKeys, szUUID), currencyNum, pError)); // Request remote wallet repo ABC_CHECK_RET(ABC_WalletServerRepoPost(L1, LP1, pData->szWalletAcctKey, ABC_SERVER_WALLET_CREATE_PATH, pError)); // set this account for the wallet's first account ABC_CHECK_RET(ABC_WalletAddAccount(ABC_WalletID(pKeys, szUUID), szUserName, pError)); // TODO: should probably add the creation date to optimize wallet export (assuming it is even used) // Init the git repo and sync it int dirty; ABC_CHECK_RET(ABC_SyncMakeRepo(pData->szWalletSyncDir, pError)); ABC_CHECK_RET(ABC_SyncRepo(pData->szWalletSyncDir, pData->szWalletAcctKey, &dirty, pError)); // Actiate the remote wallet ABC_CHECK_RET(ABC_WalletServerRepoPost(L1, LP1, pData->szWalletAcctKey, ABC_SERVER_WALLET_ACTIVATE_PATH, pError)); // If everything worked, add the wallet to the account: tABC_AccountWalletInfo info; // No need to free this info.szUUID = szUUID; info.MK = pData->MK; info.BitcoinSeed = pData->BitcoinPrivateSeed; info.SyncKey = WalletAcctKey; info.archived = 0; ABC_CHECK_RET(ABC_AccountWalletList(pKeys, NULL, &info.sortIndex, pError)); ABC_CHECK_RET(ABC_AccountWalletSave(pKeys, &info, pError)); // Now the wallet is written to disk, generate some addresses ABC_CHECK_RET(ABC_TxCreateInitialAddresses(ABC_WalletID(pKeys, pData->szUUID), pError)); // After wallet is created, sync the account, ignoring any errors tABC_Error Error; ABC_CHECK_RET(ABC_SyncRepo(pKeys->szSyncDir, pKeys->szSyncKey, &dirty, &Error)); pData = NULL; // so we don't free what we just added to the cache exit: if (cc != ABC_CC_Ok) { if (szUUID) { ABC_WalletRemoveFromCache(szUUID, NULL); } if (szWalletDir) { ABC_FileIODeleteRecursive(szWalletDir, NULL); } } ABC_FREE_STR(szWalletDir); ABC_FREE_STR(szFilename); ABC_FREE_STR(szJSON); ABC_FREE_STR(szUUID); if (pJSON_Data) json_decref(pJSON_Data); if (pJSON_Wallets) json_decref(pJSON_Wallets); if (pData) ABC_WalletFreeData(pData); return cc; }
static void ABC_BridgeTxCallback(WatcherInfo *watcherInfo, const libbitcoin::transaction_type& tx, tABC_BitCoin_Event_Callback fAsyncBitCoinEventCallback, void *pData) { tABC_CC cc = ABC_CC_Ok; tABC_Error error; int64_t fees = 0; int64_t totalInSatoshi = 0, totalOutSatoshi = 0, totalMeSatoshi = 0, totalMeInSatoshi = 0; tABC_TxOutput **iarr = NULL, **oarr = NULL; unsigned int idx = 0, iCount = 0, oCount = 0; std::string txId, malTxId; if (watcherInfo == NULL) { cc = ABC_CC_Error; goto exit; } txId = ABC_BridgeNonMalleableTxId(tx); malTxId = bc::encode_hash(bc::hash_transaction(tx)); idx = 0; iCount = tx.inputs.size(); iarr = (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()); // Check prevouts for values 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; } iarr[idx] = out; idx++; } idx = 0; oCount = tx.outputs.size(); oarr = (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(malTxId); // Do we own this address? auto row = watcherInfo->addresses.find(addr.encoded()); if (row != watcherInfo->addresses.end()) { totalMeSatoshi += o.value; } totalOutSatoshi += o.value; oarr[idx] = out; idx++; } if (totalMeSatoshi == 0 && totalMeInSatoshi == 0) { ABC_DebugLog("values == 0, this tx does not concern me.\n"); goto exit; } fees = totalInSatoshi - totalOutSatoshi; totalMeSatoshi -= totalMeInSatoshi; ABC_DebugLog("calling ABC_TxReceiveTransaction"); ABC_DebugLog("Total Me: %s, Total In: %s, Total Out: %s, Fees: %s", std::to_string(totalMeSatoshi).c_str(), std::to_string(totalInSatoshi).c_str(), std::to_string(totalOutSatoshi).c_str(), std::to_string(fees).c_str()); ABC_CHECK_RET( ABC_TxReceiveTransaction( watcherInfo->wallet, totalMeSatoshi, fees, iarr, iCount, oarr, oCount, txId.c_str(), malTxId.c_str(), fAsyncBitCoinEventCallback, pData, &error)); watcherSave(watcherInfo->wallet); // Failure is not fatal exit: ABC_FREE(oarr); ABC_FREE(iarr); }
/** * 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; }