Пример #1
0
/**
 * Gets the specified field from a json string
 */
tABC_CC ABC_UtilGetIntValueFromJSONString(const char *szJSON,
                                          const char *szFieldName,
                                          int       *pValue,
                                          tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;

    json_t *pJSON_Root = NULL;
    json_t *pJSON_Value = NULL;

    ABC_CHECK_NULL(szJSON);
    ABC_CHECK_NULL(szFieldName);
    ABC_CHECK_NULL(pValue);

    // decode the object
    json_error_t error;
    pJSON_Root = json_loads(szJSON, 0, &error);
    ABC_CHECK_ASSERT(pJSON_Root != NULL, ABC_CC_JSONError, "Error parsing JSON");
    ABC_CHECK_ASSERT(json_is_object(pJSON_Root), ABC_CC_JSONError, "Error parsing JSON");

    // get the field
    pJSON_Value = json_object_get(pJSON_Root, szFieldName);
    ABC_CHECK_ASSERT((pJSON_Value && json_is_number(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON int value");
    *pValue = (int) json_integer_value(pJSON_Value);

exit:
    if (pJSON_Root) json_decref(pJSON_Root);

    return cc;
}
Пример #2
0
/**
 * Gets the specified field from a json string
 * the user is responsible for free'ing the value
 */
tABC_CC ABC_UtilGetStringValueFromJSONString(const char *szJSON,
                                             const char *szFieldName,
                                             char       **pszValue,
                                             tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;

    json_t *pJSON_Root = NULL;
    json_t *pJSON_Value = NULL;

    ABC_CHECK_NULL(szJSON);
    ABC_CHECK_NULL(szFieldName);
    ABC_CHECK_NULL(pszValue);

    // decode the object
    json_error_t error;
    pJSON_Root = json_loads(szJSON, 0, &error);
    ABC_CHECK_ASSERT(pJSON_Root != NULL, ABC_CC_JSONError, "Error parsing JSON");
    ABC_CHECK_ASSERT(json_is_object(pJSON_Root), ABC_CC_JSONError, "Error parsing JSON");

    // get the field
    pJSON_Value = json_object_get(pJSON_Root, szFieldName);
    ABC_CHECK_ASSERT((pJSON_Value && json_is_string(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON string value");
    ABC_STRDUP(*pszValue, json_string_value(pJSON_Value));

exit:
    if (pJSON_Root) json_decref(pJSON_Root);

    return cc;
}
Пример #3
0
/**
 * Gets the specified field from a json string
 * the user is responsible for free'ing the array
 */
tABC_CC ABC_UtilGetArrayValuesFromJSONString(const char *szJSON,
                                             const char *szFieldName,
                                             char       ***aszValues,
                                             unsigned int *pCount,
                                             tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;

    json_t *pJSON_Root = NULL;
    json_t *pJSON_Value = NULL;
    char **pArrayStrings = NULL;

    ABC_CHECK_NULL(szJSON);
    ABC_CHECK_NULL(szFieldName);
    ABC_CHECK_NULL(aszValues);
    *aszValues = NULL;
    ABC_CHECK_NULL(pCount);
    *pCount = 0;

    // decode the object
    json_error_t error;
    pJSON_Root = json_loads(szJSON, 0, &error);
    ABC_CHECK_ASSERT(pJSON_Root != NULL, ABC_CC_JSONError, "Error parsing JSON");
    ABC_CHECK_ASSERT(json_is_object(pJSON_Root), ABC_CC_JSONError, "Error parsing JSON");

    // get the field
    pJSON_Value = json_object_get(pJSON_Root, szFieldName);
    ABC_CHECK_ASSERT((pJSON_Value && json_is_array(pJSON_Value)), ABC_CC_JSONError, "Error parsing JSON array value");

    // get the number of elements in the array
    *pCount = (int) json_array_size(pJSON_Value);

    if (*pCount > 0)
    {
        ABC_ARRAY_NEW(pArrayStrings, *pCount, char*);

        for (unsigned i = 0; i < *pCount; i++)
        {
            json_t *pJSON_Elem = json_array_get(pJSON_Value, i);
            ABC_CHECK_ASSERT((pJSON_Elem && json_is_string(pJSON_Elem)), ABC_CC_JSONError, "Error parsing JSON string value");
            ABC_STRDUP(pArrayStrings[i], json_string_value(pJSON_Elem));
        }

        *aszValues = pArrayStrings;
        pArrayStrings = NULL;
    }
Пример #4
0
/**
 * Serializes a LoginPackage object to a JSON string.
 */
tABC_CC ABC_LoginPackageEncode(tABC_LoginPackage *pSelf,
                               char **pszLoginPackage,
                               tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;
    json_t  *pJSON_Root     = NULL;

    // Build the main body:
    pJSON_Root = json_object();
    ABC_CHECK_NULL(pJSON_Root);

    // Write master key:
    if (pSelf->EMK_LP2)
    {
        ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_EMK_LP2_FIELD, pSelf->EMK_LP2));
    }
    if (pSelf->EMK_LRA3)
    {
        ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_EMK_LRA3_FIELD, pSelf->EMK_LRA3));
    }

    // Write sync key:
    ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_ESYNCKEY_FIELD, pSelf->ESyncKey));

    // Write server keys:
    if (pSelf->ELP1)
    {
        ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_ELP1_FIELD, pSelf->ELP1));
    }
    if (pSelf->ELRA1)
    {
        ABC_CHECK_JSON(json_object_set(pJSON_Root, JSON_ACCT_ELRA1_FIELD, pSelf->ELRA1));
    }

    // Write out:
    *pszLoginPackage = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_INDENT(4) | JSON_PRESERVE_ORDER);
    ABC_CHECK_NULL(*pszLoginPackage);

exit:
    if (pJSON_Root)     json_decref(pJSON_Root);

    return cc;
}
Пример #5
0
/**
 * 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;
}
Пример #6
0
/**
 *  Creates a JSON string representing an array of values with their name
 */
tABC_CC ABC_UtilCreateArrayJSONObject(char   **aszValues,
                                      unsigned int count,
                                      const char   *szFieldName,
                                      json_t       **ppJSON_Data,
                                      tABC_Error   *pError)
{
    tABC_CC cc = ABC_CC_Ok;

    json_t *jsonItems = NULL;
    json_t *jsonItemArray = NULL;

    ABC_CHECK_NULL(szFieldName);
    ABC_CHECK_NULL(ppJSON_Data);

    // create the json object that will be our questions
    jsonItems = json_object();
    jsonItemArray = json_array();

    // if there are values
    if ((count > 0) && (aszValues != NULL))
    {
        for (unsigned i = 0; i < count; i++)
        {
            json_array_append_new(jsonItemArray, json_string(aszValues[i]));
        }
    }

    // set our final json for the array element
    json_object_set(jsonItems, szFieldName, jsonItemArray);

    *ppJSON_Data = jsonItems;
    jsonItems = NULL;

exit:
    if (jsonItems)      json_decref(jsonItems);
    if (jsonItemArray)  json_decref(jsonItemArray);

    return cc;
}
Пример #7
0
/**
 * Creates the json package with a single field and its int value
 */
tABC_CC ABC_UtilCreateIntJSONString(int        value,
                                    const char *szFieldName,
                                    char       **pszJSON,
                                    tABC_Error *pError)
{
    tABC_CC cc = ABC_CC_Ok;

    json_t *pJSON_Root = NULL;

    ABC_CHECK_NULL(szFieldName);
    ABC_CHECK_NULL(pszJSON);

    // create the jansson object
    pJSON_Root = json_pack("{si}", szFieldName, value);

    *pszJSON = ABC_UtilStringFromJSONObject(pJSON_Root, JSON_INDENT(4) | JSON_PRESERVE_ORDER);

exit:
    if (pJSON_Root) json_decref(pJSON_Root);

    return cc;
}
Пример #8
0
/**
 * 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*);
    }
Пример #9
0
/**
 * 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;
}
Пример #10
0
/**
 * 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*);
        }
Пример #11
0
/**
 * 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;
}