예제 #1
0
Status
AddressDb::getNew(Address &result)
{
    std::lock_guard<std::mutex> lock(mutex_);

    // Use a set to find the lowest available index:
    std::set<size_t> indices;
    for (auto &address: addresses_)
        if (address.second.recyclable)
            indices.insert(address.second.index);

    // The stockpile should prevent this from ever happening:
    if (indices.empty())
        return ABC_ERROR(ABC_CC_NoAvailableAddress, "Address stockpile depleted!");
    size_t index = *indices.begin();

    // Verify that we can still re-derive the address:
    auto i = addresses_.find(mainBranch(wallet_).generate_private_key(index).
                             address().encoded());
    if (addresses_.end() == i)
        return ABC_ERROR(ABC_CC_Error,
                         "Address corruption at index " + std::to_string(index));

    result = i->second;
    return Status();
}
예제 #2
0
/**
 * Checks an hits key for validity.
 */
static Status
hbitsOk(const std::string &text)
{
    if (text.size() != 22 && text.size() != 30)
        return ABC_ERROR(ABC_CC_ParseError, "Wrong text length");
    if (0x00 != bc::sha256_hash(bc::to_data_chunk(text + "!"))[0])
        return ABC_ERROR(ABC_CC_ParseError, "Wrong text checksum");
    return Status();
}
예제 #3
0
Status
loginServerOtpPending(std::list<DataChunk> users, std::list<bool> &isPending)
{
    const auto url = ABC_SERVER_ROOT "/v1/otp/pending/check";

    std::string param;
    std::map<std::string, bool> userMap;
    std::list<std::string> usersEncoded;
    for (const auto &u : users)
    {
        std::string username = base64Encode(u);
        param += (username + ",");
        userMap[username] = false;
        usersEncoded.push_back(username);
    }
    JsonObject json;
    ABC_CHECK(json.set("l1s", param));

    HttpReply reply;
    ABC_CHECK(AirbitzRequest().post(reply, url, json.encode()));
    ServerReplyJson replyJson;
    ABC_CHECK(replyJson.decode(reply));

    JsonArray arrayJson = replyJson.results();
    size_t size = arrayJson.size();
    for (size_t i = 0; i < size; i++)
    {
        json_t *pJSON_Row = arrayJson[i].get();
        if (!pJSON_Row || !json_is_object(pJSON_Row))
            return ABC_ERROR(ABC_CC_JSONError, "Error parsing JSON array element object");

        json_t *pJSON_Value = json_object_get(pJSON_Row, "login");
        if (!pJSON_Value || !json_is_string(pJSON_Value))
            return ABC_ERROR(ABC_CC_JSONError, "Error otp/pending/login JSON");
        std::string username(json_string_value(pJSON_Value));

        pJSON_Value = json_object_get(pJSON_Row, ABC_SERVER_JSON_OTP_PENDING);
        if (!pJSON_Value || !json_is_boolean(pJSON_Value))
            return ABC_ERROR(ABC_CC_JSONError, "Error otp/pending/pending JSON");
        if (json_is_true(pJSON_Value))
        {
            userMap[username] = json_is_true(pJSON_Value);
        }
    }
    isPending.clear();
    for (auto &username: usersEncoded)
    {
        isPending.push_back(userMap[username]);
    }

    return Status();
}
예제 #4
0
Status
JsonFile::save(const std::string &filename) const
{
    if (json_dump_file(root_, filename.c_str(), saveFlags))
        return ABC_ERROR(ABC_CC_JSONError, "Cannot write JSON file " + filename);
    return Status();
}
예제 #5
0
Status
JsonBox::decrypt(DataChunk &result, DataSlice key)
{
    DataChunk nonce;
    ABC_CHECK(nonceOk());
    ABC_CHECK(base16Decode(nonce, this->nonce()));

    DataChunk cyphertext;
    ABC_CHECK(cyphertextOk());
    ABC_CHECK(base64Decode(cyphertext, this->cyphertext()));

    switch (type())
    {
    case AES256_CBC_AIRBITZ:
    {
        ABC_CHECK_OLD(ABC_CryptoDecryptAES256Package(result,
                      cyphertext, key, nonce,
                      &error));
        return Status();
    }

    default:
        return ABC_ERROR(ABC_CC_DecryptError, "Unknown encryption type");
    }
}
예제 #6
0
Status
chunkDecode(DataChunk &result, const std::string &in)
{
    // The string must be a multiple of the chunk size:
    if (in.size() % Chars)
        return ABC_ERROR(ABC_CC_ParseError, "Bad encoding");

    DataChunk out;
    out.reserve(Bytes * (in.size() / Chars));

    constexpr unsigned shift = 8 * Bytes / Chars; // Bits per character
    uint16_t buffer = 0; // Bits waiting to be written out, MSB first
    int bits = 0; // Number of bits currently in the buffer
    auto i = in.begin();
    while (i != in.end())
    {
        // Read one character from the string:
        int value = Decode(*i);
        if (value < 0)
            break;
        ++i;

        // Append the bits to the buffer:
        buffer |= value << (16 - bits - shift);
        bits += shift;

        // Write out some bits if the buffer has a byte's worth:
        if (8 <= bits)
        {
            out.push_back(buffer >> 8);
            buffer <<= 8;
            bits -= 8;
        }
    }
예제 #7
0
Status
HttpReply::codeOk() const
{
    if (code < 200 || 300 <= code)
        return ABC_ERROR(ABC_CC_Error, "Bad HTTP status code " +
                         std::to_string(code));
    return Status();
}
예제 #8
0
Status
Login::repoFind(JsonPtr &result, const std::string &type, bool create)
{
    result = JsonPtr();

    // Try 1: Use what we have:
    repoFindLocal(result, type).log(); // Failure is fine
    if (result) return Status();

    // Try 2: Sync with the server:
    ABC_CHECK(update());
    repoFindLocal(result, type).log(); // Failure is fine
    if (result) return Status();

    // Try 3: Make a new repo:
    if (create)
    {
        // Make the keys:
        DataChunk dataKey;
        DataChunk syncKey;
        ABC_CHECK(randomData(dataKey, DATA_KEY_LENGTH));
        ABC_CHECK(randomData(syncKey, SYNC_KEY_LENGTH));
        AccountRepoJson repoJson;
        ABC_CHECK(repoJson.syncKeySet(base64Encode(syncKey)));
        ABC_CHECK(repoJson.dataKeySet(base64Encode(dataKey_)));

        // Make the metadata:
        DataChunk id;
        ABC_CHECK(randomData(id, keyIdLength));
        KeyJson keyJson;
        ABC_CHECK(keyJson.idSet(base64Encode(id)));
        ABC_CHECK(keyJson.typeSet(type));
        ABC_CHECK(keyJson.keysSet(repoJson));

        // Encrypt the metadata:
        JsonBox keyBox;
        ABC_CHECK(keyBox.encrypt(keyJson.encode(), dataKey_));

        // Push the wallet to the server:
        AuthJson authJson;
        ABC_CHECK(authJson.loginSet(*this));
        ABC_CHECK(loginServerKeyAdd(authJson, keyBox, base16Encode(syncKey)));

        // Save to disk:
        LoginStashJson stashJson;
        ABC_CHECK(stashJson.load(paths.stashPath()));
        if (!stashJson.keyBoxes().ok())
            ABC_CHECK(stashJson.keyBoxesSet(JsonArray()));
        ABC_CHECK(stashJson.keyBoxes().append(keyBox));
        ABC_CHECK(stashJson.save(paths.stashPath()));

        result = repoJson;
        return Status();
    }

    return ABC_ERROR(ABC_CC_AccountDoesNotExist, "No such repo");
}
AirbitzRequest::AirbitzRequest()
{
    if (!status_)
        return;

    if (curl_easy_setopt(handle_, CURLOPT_SSL_CTX_FUNCTION, curlSslCallback))
        status_ = ABC_ERROR(ABC_CC_Error, "cURL failed to set SSL pinning");
    header("Content-Type", "application/json");
    header("Authorization", API_KEY_HEADER + 15);
}
예제 #10
0
Status
JsonFile::load(const std::string &filename)
{
    json_error_t error;
    json_t *root = json_load_file(filename.c_str(), loadFlags, &error);
    if (!root)
        return ABC_ERROR(ABC_CC_JSONError, error.text);
    reset(root);
    return Status();
}
예제 #11
0
Status
JsonFile::encode(std::string &result) const
{
    char *raw = json_dumps(root_, saveFlags);
    if (!raw)
        return ABC_ERROR(ABC_CC_JSONError, "Cannot encode JSON.");
    result = raw;
    ABC_UtilJanssonSecureFree(raw);
    return Status();
}
예제 #12
0
Status
JsonFile::decode(const std::string &data)
{
    json_error_t error;
    json_t *root = json_loadb(data.data(), data.size(), loadFlags, &error);
    if (!root)
        return ABC_ERROR(ABC_CC_JSONError, error.text);
    reset(root);
    return Status();
}
예제 #13
0
Status
bridgeWatcherStart(Wallet &self)
{
    if (watchers_.end() != watchers_.find(self.id()))
        return ABC_ERROR(ABC_CC_Error,
                         "Watcher already exists for " + self.id());

    watchers_[self.id()].reset(new WatcherInfo(self));

    return Status();
}
예제 #14
0
static Status
watcherFind(WatcherInfo *&result, Wallet &self)
{
    std::string id = self.id();
    auto row = watchers_.find(id);
    if (row == watchers_.end())
        return ABC_ERROR(ABC_CC_Synchronizing, "Cannot find watcher for " + id);

    result = row->second.get();
    return Status();
}
예제 #15
0
void
StratumConnection::getTx(
    const bc::client::obelisk_codec::error_handler &onError,
    const bc::client::obelisk_codec::fetch_transaction_handler &onReply,
    const bc::hash_digest &txid)
{
    JsonArray params;
    params.append(json_string(bc::encode_hash(txid).c_str()));

    auto errorShim = [onError](Status status)
    {
        onError(std::make_error_code(std::errc::bad_message));
    };

    auto decoder = [onReply](JsonPtr payload) -> Status
    {
        if (!json_is_string(payload.get()))
            return ABC_ERROR(ABC_CC_JSONError, "Bad reply format");

        bc::data_chunk rawTx;
        if (!base16Decode(rawTx, json_string_value(payload.get())))
            return ABC_ERROR(ABC_CC_ParseError, "Bad transaction format");

        // Convert rawTx to bc::transaction_type:
        bc::transaction_type tx;
        try
        {
            auto deserial = bc::make_deserializer(rawTx.begin(), rawTx.end());
            bc::satoshi_load(deserial.iterator(), deserial.end(), tx);
        }
        catch (bc::end_of_stream)
        {
            return ABC_ERROR(ABC_CC_ParseError, "Bad transaction format");
        }

        onReply(tx);
        return Status();
    };

    sendMessage("blockchain.transaction.get", params, errorShim, decoder);
}
예제 #16
0
Status
AddressDb::get(Address &result, const std::string &address)
{
    std::lock_guard<std::mutex> lock(mutex_);

    auto i = addresses_.find(address);
    if (i == addresses_.end())
        return ABC_ERROR(ABC_CC_NoAvailableAddress, "No address: " + address);

    result = i->second;
    return Status();
}
예제 #17
0
Status
randomData(DataChunk &result, size_t size)
{
    DataChunk out;
    out.resize(size);

    if (!RAND_bytes(out.data(), out.size()))
        return ABC_ERROR(ABC_CC_Error, "Random data generation failed");

    result = std::move(out);
    return Status();
}
예제 #18
0
static Status curlOk(CURLcode code)
{
    if (code)
    {
        std::string message("cURL error: ");
        if (curl_easy_strerror(code))
            message += curl_easy_strerror(code);
        else
            message += std::to_string(code);
        return ABC_ERROR(ABC_CC_SysError, message);
    }
    return Status();
}
예제 #19
0
Status
ScryptSnrp::hash(DataChunk &result, DataSlice data, size_t size) const
{
    DataChunk out(size);

    int rc = crypto_scrypt(data.data(), data.size(),
                           salt.data(), salt.size(), n, r, p, out.data(), size);
    if (rc)
        return ABC_ERROR(ABC_CC_ScryptError, "Error calculating Scrypt hash");

    result = std::move(out);
    return Status();
}
예제 #20
0
static Status
watcherLoad(Wallet &self)
{
    Watcher *watcher = nullptr;
    ABC_CHECK(watcherFind(watcher, self));

    DataChunk data;
    ABC_CHECK(fileLoad(data, watcherPath(self)));
    if (!watcher->load(data))
        return ABC_ERROR(ABC_CC_Error, "Unable to load serialized watcher");

    return Status();
}
예제 #21
0
HttpRequest &
HttpRequest::header(const std::string &key, const std::string &value)
{
    if (!status_)
        return *this;

    std::string header = key + ": " + value;
    auto slist = curl_slist_append(headers_, header.c_str());
    if (!slist)
        status_ = ABC_ERROR(ABC_CC_Error, "cURL slist error");
    else
        headers_ = slist;

    return *this;
}
예제 #22
0
Status
currencyNumber(Currency &result, const std::string &code)
{
    static const std::map<std::string, Currency> map
    {
        ABC_CURRENCY_LIST(CURRENCY_NUMBER_ROW)
    };

    auto i = map.find(code);
    if (map.end() == i)
        return ABC_ERROR(ABC_CC_ParseError, "Cannot find currency code " + code);

    result = i->second;
    return Status();
}
예제 #23
0
Status
currencyName(std::string &result, Currency number)
{
    static const std::map<Currency, const char *> map
    {
        ABC_CURRENCY_LIST(CURRENCY_NAME_ROW)
    };

    auto i = map.find(number);
    if (map.end() == i)
        return ABC_ERROR(ABC_CC_ParseError, "Cannot find currency number");

    result = i->second;
    return Status();
}
예제 #24
0
static Status
blockchainPostTx(DataSlice tx)
{
    std::string body = "tx=" + base16Encode(tx);
    if (isTestnet())
        return ABC_ERROR(ABC_CC_Error, "No blockchain.info testnet");

    HttpReply reply;
    ABC_CHECK(HttpRequest().
              header("Content-Type", "application/x-www-form-urlencoded").
              post(reply, "https://blockchain.info/pushtx", body));
    ABC_CHECK(reply.codeOk());

    return Status();
}
예제 #25
0
Status
pluginDataGet(const Account &account, const std::string &plugin,
              const std::string &key, std::string &data)
{
    PluginDataFile json;
    ABC_CHECK(json.load(keyFilename(account, plugin, key),
                        account.login.dataKey()));
    ABC_CHECK(json.keyOk());
    ABC_CHECK(json.dataOk());

    if (json.key() != key)
        return ABC_ERROR(ABC_CC_JSONError, "Plugin filename does not match contents");

    data = json.data();
    return Status();
}
예제 #26
0
Status
debugLogRotate()
{
    if (gLogFile)
        fclose(gLogFile);

    auto path = debugLogPath();
    if (fileExists(path))
        rename(path.c_str(), debugLogOldPath().c_str());

    gLogFile = fopen(path.c_str(), "w");
    if (!gLogFile)
        return ABC_ERROR(ABC_CC_SysError, "Cannot open " + path);

    return Status();
}
예제 #27
0
Status
watcherBridgeRawTx(Wallet &self, const char *szTxID,
    DataChunk &result)
{
    Watcher *watcher = nullptr;
    ABC_CHECK(watcherFind(watcher, self));

    bc::hash_digest txid;
    if (!bc::decode_hash(txid, szTxID))
        return ABC_ERROR(ABC_CC_ParseError, "Bad txid");
    auto tx = watcher->find_tx(txid);
    result.resize(satoshi_raw_size(tx));
    bc::satoshi_save(tx, result.begin());

    return Status();
}
예제 #28
0
Status
Login::repoFindLocal(JsonPtr &result, const std::string &type)
{
    // If this is an Airbitz account, try the legacy `syncKey`:
    if (repoTypeAirbitzAccount == type)
    {
        LoginPackage loginPackage;
        ABC_CHECK(loginPackage.load(paths.loginPackagePath()));
        if (loginPackage.syncKeyBox().ok())
        {
            DataChunk syncKey;
            ABC_CHECK(loginPackage.syncKeyBox().decrypt(syncKey, dataKey_));

            AccountRepoJson repoJson;
            ABC_CHECK(repoJson.syncKeySet(base64Encode(syncKey)));
            ABC_CHECK(repoJson.dataKeySet(base64Encode(dataKey_)));

            result = repoJson;
            return Status();
        }
    }

    // Search the on-disk array:
    LoginStashJson stashJson;
    if (stashJson.load(paths.stashPath()))
    {
        auto keyBoxesJson = stashJson.keyBoxes();
        size_t keyBoxesSize = keyBoxesJson.size();
        for (size_t i = 0; i < keyBoxesSize; i++)
        {
            JsonBox boxJson(keyBoxesJson[i]);

            DataChunk keyBytes;
            KeyJson keyJson;
            ABC_CHECK(boxJson.decrypt(keyBytes, dataKey_));
            ABC_CHECK(keyJson.decode(toString(keyBytes)));

            if (keyJson.typeOk() && type == keyJson.type())
            {
                result = keyJson.keys();
                return Status();
            }
        }
    }

    return ABC_ERROR(ABC_CC_AccountDoesNotExist, "No such repo");
}
예제 #29
0
void
StratumConnection::getAddressHistory(
    const bc::client::obelisk_codec::error_handler &onError,
    const bc::client::obelisk_codec::fetch_history_handler &onReply,
    const bc::payment_address &address, size_t fromHeight)
{
    JsonArray params;
    params.append(json_string(address.encoded().c_str()));

    auto errorShim = [onError](Status status)
    {
        onError(std::make_error_code(std::errc::bad_message));
    };

    auto decoder = [onReply](JsonPtr payload) -> Status
    {
        JsonArray arrayJson(payload);

        bc::client::history_list history;
        size_t size = arrayJson.size();
        history.reserve(size);
        for (size_t i = 0; i < size; i++)
        {
            struct HistoryJson:
                public JsonObject
            {
                ABC_JSON_CONSTRUCTORS(HistoryJson, JsonObject)
                ABC_JSON_STRING(txid, "tx_hash", nullptr)
                ABC_JSON_INTEGER(height, "height", 0)
            };
            HistoryJson json(arrayJson[i]);

            bc::hash_digest hash;
            if (!json.txidOk() || !bc::decode_hash(hash, json.txid()))
                return ABC_ERROR(ABC_CC_Error, "Bad txid");

            bc::client::history_row row;
            row.output.hash = hash;
            row.output_height = json.height();
            row.spend.hash = bc::null_hash;
            history.push_back(row);
        }

        onReply(history);
        return Status();
    };
예제 #30
0
Status
HttpRequest::init()
{
    handle_ = curl_easy_init();
    if (!handle_)
        return ABC_ERROR(ABC_CC_Error, "cURL failed create handle");

    // Basic options:
    ABC_CHECK_CURL(curl_easy_setopt(handle_, CURLOPT_NOSIGNAL, 1));
    ABC_CHECK_CURL(curl_easy_setopt(handle_, CURLOPT_CONNECTTIMEOUT, TIMEOUT));

    const auto certPath = gContext->paths.certPath();
    if (!certPath.empty())
        ABC_CHECK_CURL(curl_easy_setopt(handle_, CURLOPT_CAINFO,
                                        certPath.c_str()));

    return Status();
}