Store creator hash prefixes in a separate lookup file (#27731)

[Rewards] Store creator hash prefixes in a separate lookup file
This commit is contained in:
Kevin Smith
2025-03-05 09:27:29 -05:00
committed by GitHub
parent 10cc0ec933
commit e4e11d831b
38 changed files with 803 additions and 493 deletions
@@ -42,6 +42,7 @@
#include "brave/components/brave_rewards/content/service_sandbox_type.h"
#include "brave/components/brave_rewards/core/buildflags/buildflags.h"
#include "brave/components/brave_rewards/core/engine/global_constants.h"
#include "brave/components/brave_rewards/core/engine/hash_prefix_store.h"
#include "brave/components/brave_rewards/core/engine/parameters/rewards_parameters_provider.h"
#include "brave/components/brave_rewards/core/engine/rewards_database.h"
#include "brave/components/brave_rewards/core/features.h"
@@ -235,12 +236,14 @@ void DeferCallback(base::Location location, Callback callback, Args&&... args) {
// read comment about file pathes at src\base\files\file_path.h
#if BUILDFLAG(IS_WIN)
const base::FilePath::StringType kCreatorPrefixStore(L"RewardsCreators.db");
const base::FilePath::StringType kDiagnosticLogPath(L"Rewards.log");
const base::FilePath::StringType kLegacy_state(L"ledger_state");
const base::FilePath::StringType kPublisher_state(L"publisher_state");
const base::FilePath::StringType kPublisher_info_db(L"publisher_info_db");
const base::FilePath::StringType kPublishers_list(L"publishers_list");
#else
const base::FilePath::StringType kCreatorPrefixStore("RewardsCreators.db");
const base::FilePath::StringType kDiagnosticLogPath("Rewards.log");
const base::FilePath::StringType kLegacy_state("ledger_state");
const base::FilePath::StringType kPublisher_state("publisher_state");
@@ -272,6 +275,7 @@ RewardsServiceImpl::RewardsServiceImpl(
publisher_state_path_(profile_path.Append(kPublisher_state)),
publisher_info_db_path_(profile_path.Append(kPublisher_info_db)),
publisher_list_path_(profile_path.Append(kPublishers_list)),
creator_prefix_store_path_(profile_path.Append(kCreatorPrefixStore)),
diagnostic_log_(new DiagnosticLog(profile_path.Append(kDiagnosticLogPath),
kDiagnosticLogMaxFileSize,
kDiagnosticLogKeepNumLines)),
@@ -386,6 +390,9 @@ void RewardsServiceImpl::StartEngineProcessIfNecessary() {
rewards_database_ = base::SequenceBound<internal::RewardsDatabase>(
file_task_runner_, publisher_info_db_path_);
creator_prefix_store_ = base::SequenceBound<internal::HashPrefixStore>(
file_task_runner_, creator_prefix_store_path_);
BLOG(1, "Starting engine process");
if (!engine_factory_.is_bound()) {
@@ -1066,10 +1073,8 @@ void RewardsServiceImpl::OnDiagnosticLogDeletedForCompleteReset(
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const std::vector<base::FilePath> paths = {
legacy_state_path_,
publisher_state_path_,
publisher_info_db_path_,
publisher_list_path_,
legacy_state_path_, publisher_state_path_, publisher_info_db_path_,
publisher_list_path_, creator_prefix_store_path_,
};
file_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&DeleteFilesOnFileTaskRunner, paths),
@@ -1092,6 +1097,7 @@ void RewardsServiceImpl::Reset() {
engine_factory_.reset();
ready_ = std::make_unique<base::OneShotEvent>();
rewards_database_.Reset();
creator_prefix_store_.Reset();
BLOG(1, "Successfully reset rewards service");
}
@@ -2103,6 +2109,22 @@ void RewardsServiceImpl::OnRunDBTransaction(
std::move(callback).Run(std::move(response));
}
void RewardsServiceImpl::UpdateCreatorPrefixStore(
mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) {
creator_prefix_store_.AsyncCall(&internal::HashPrefixStore::UpdatePrefixes)
.WithArgs(std::move(prefix_data->prefixes), prefix_data->prefix_size)
.Then(std::move(callback));
}
void RewardsServiceImpl::CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) {
creator_prefix_store_.AsyncCall(&internal::HashPrefixStore::ContainsPrefix)
.WithArgs(value)
.Then(std::move(callback));
}
void RewardsServiceImpl::ForTestingSetTestResponseCallback(
const GetTestResponseCallback& callback) {
test_response_callback_ = callback;
@@ -26,6 +26,7 @@
#include "brave/components/brave_rewards/content/diagnostic_log.h"
#include "brave/components/brave_rewards/content/rewards_p3a.h"
#include "brave/components/brave_rewards/content/rewards_service.h"
#include "brave/components/brave_rewards/core/mojom/rewards_database.mojom.h"
#include "brave/components/brave_rewards/core/mojom/rewards_engine.mojom.h"
#include "brave/components/brave_rewards/core/rewards_flags.h"
#include "brave/components/brave_wallet/browser/brave_wallet_service.h"
@@ -60,6 +61,7 @@ namespace brave_rewards {
class RewardsFlagBrowserTest;
namespace internal {
class HashPrefixStore;
class RewardsDatabase;
} // namespace internal
@@ -429,6 +431,14 @@ class RewardsServiceImpl final : public RewardsService,
void RunDBTransaction(mojom::DBTransactionPtr transaction,
RunDBTransactionCallback callback) override;
void UpdateCreatorPrefixStore(
mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) override;
void CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) override;
void ClearAllNotifications() override;
void ExternalWalletConnected() override;
@@ -502,9 +512,11 @@ class RewardsServiceImpl final : public RewardsService,
const base::FilePath publisher_state_path_;
const base::FilePath publisher_info_db_path_;
const base::FilePath publisher_list_path_;
const base::FilePath creator_prefix_store_path_;
std::unique_ptr<DiagnosticLog> diagnostic_log_;
base::SequenceBound<internal::RewardsDatabase> rewards_database_;
base::SequenceBound<internal::HashPrefixStore> creator_prefix_store_;
std::unique_ptr<RewardsNotificationServiceImpl> notification_service_;
std::unique_ptr<RewardsServiceObserver> extension_observer_;
+1
View File
@@ -33,6 +33,7 @@ static_library("core") {
"//base",
"//brave/components/l10n/common",
"//components/prefs",
"//mojo/public/mojom/base",
"//net",
"//url",
]
@@ -146,8 +146,6 @@ static_library("engine") {
"database/database_migration.h",
"database/database_publisher_info.cc",
"database/database_publisher_info.h",
"database/database_publisher_prefix_list.cc",
"database/database_publisher_prefix_list.h",
"database/database_recurring_tip.cc",
"database/database_recurring_tip.h",
"database/database_server_publisher_banner.cc",
@@ -203,6 +201,7 @@ static_library("engine") {
"database/migration/migration_v39.h",
"database/migration/migration_v4.h",
"database/migration/migration_v40.h",
"database/migration/migration_v41.h",
"database/migration/migration_v5.h",
"database/migration/migration_v6.h",
"database/migration/migration_v7.h",
@@ -299,6 +298,9 @@ static_library("engine") {
"endpoints/zebpay/post_oauth_zebpay.h",
"gemini/gemini.cc",
"gemini/gemini.h",
"hash_prefix_iterator.h",
"hash_prefix_store.cc",
"hash_prefix_store.h",
"initialization_manager.cc",
"initialization_manager.h",
"legacy/bat_state.cc",
@@ -329,7 +331,6 @@ static_library("engine") {
"publisher/media/media.h",
"publisher/media/youtube.cc",
"publisher/media/youtube.h",
"publisher/prefix_iterator.h",
"publisher/prefix_list_reader.cc",
"publisher/prefix_list_reader.h",
"publisher/prefix_util.cc",
@@ -365,6 +366,8 @@ static_library("engine") {
"state/state_migration_v13.h",
"state/state_migration_v14.cc",
"state/state_migration_v14.h",
"state/state_migration_v15.cc",
"state/state_migration_v15.h",
"state/state_migration_v2.cc",
"state/state_migration_v2.h",
"state/state_migration_v3.cc",
@@ -26,7 +26,6 @@ Database::Database(RewardsEngine& engine)
external_transactions_(engine),
media_publisher_info_(engine),
publisher_info_(engine),
publisher_prefix_list_(engine),
recurring_tip_(engine),
server_publisher_info_(engine),
sku_order_(engine),
@@ -358,17 +357,6 @@ void Database::RemoveRecurringTip(const std::string& publisher_key,
/**
* SERVER PUBLISHER INFO
*/
void Database::SearchPublisherPrefixList(
const std::string& publisher_prefix,
SearchPublisherPrefixListCallback callback) {
publisher_prefix_list_.Search(publisher_prefix, std::move(callback));
}
void Database::ResetPublisherPrefixList(publisher::PrefixListReader reader,
ResultCallback callback) {
publisher_prefix_list_.Reset(std::move(reader), std::move(callback));
}
void Database::InsertServerPublisherInfo(
const mojom::ServerPublisherInfo& server_info,
ResultCallback callback) {
@@ -26,13 +26,11 @@
#include "brave/components/brave_rewards/core/engine/database/database_initialize.h"
#include "brave/components/brave_rewards/core/engine/database/database_media_publisher_info.h"
#include "brave/components/brave_rewards/core/engine/database/database_publisher_info.h"
#include "brave/components/brave_rewards/core/engine/database/database_publisher_prefix_list.h"
#include "brave/components/brave_rewards/core/engine/database/database_recurring_tip.h"
#include "brave/components/brave_rewards/core/engine/database/database_server_publisher_info.h"
#include "brave/components/brave_rewards/core/engine/database/database_sku_order.h"
#include "brave/components/brave_rewards/core/engine/database/database_sku_transaction.h"
#include "brave/components/brave_rewards/core/engine/database/database_unblinded_token.h"
#include "brave/components/brave_rewards/core/engine/publisher/prefix_list_reader.h"
#include "brave/components/brave_rewards/core/engine/rewards_callbacks.h"
namespace brave_rewards::internal {
@@ -232,12 +230,6 @@ class Database {
/**
* SERVER PUBLISHER INFO
*/
void SearchPublisherPrefixList(const std::string& publisher_key,
SearchPublisherPrefixListCallback callback);
void ResetPublisherPrefixList(publisher::PrefixListReader reader,
ResultCallback callback);
void InsertServerPublisherInfo(const mojom::ServerPublisherInfo& server_info,
ResultCallback callback);
@@ -317,7 +309,6 @@ class Database {
DatabaseExternalTransactions external_transactions_;
DatabaseMediaPublisherInfo media_publisher_info_;
DatabasePublisherInfo publisher_info_;
DatabasePublisherPrefixList publisher_prefix_list_;
DatabaseRecurringTip recurring_tip_;
DatabaseServerPublisherInfo server_publisher_info_;
DatabaseSKUOrder sku_order_;
@@ -47,6 +47,7 @@
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v39.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v4.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v40.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v41.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v5.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v6.h"
#include "brave/components/brave_rewards/core/engine/database/migration/migration_v7.h"
@@ -140,7 +141,8 @@ void DatabaseMigration::Start(uint32_t table_version, ResultCallback callback) {
migration::v37,
migration::v38,
migration::v39,
migration::v40};
migration::v40,
migration::v41};
DCHECK_LE(target_version, mappings.size());
@@ -829,4 +829,11 @@ TEST_F(RewardsDatabaseMigrationTest, Migration_40) {
EXPECT_FALSE(GetDB()->DoesTableExist("processed_publisher"));
}
TEST_F(RewardsDatabaseMigrationTest, Migration_41) {
DatabaseMigration::SetTargetVersionForTesting(41);
InitializeDatabaseAtVersion(39);
InitializeEngine();
EXPECT_FALSE(GetDB()->DoesTableExist("publisher_prefix_list"));
}
} // namespace brave_rewards::internal
@@ -1,168 +0,0 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_rewards/core/engine/database/database_publisher_prefix_list.h"
#include <optional>
#include <tuple>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "brave/components/brave_rewards/core/engine/database/database_util.h"
#include "brave/components/brave_rewards/core/engine/publisher/prefix_util.h"
#include "brave/components/brave_rewards/core/engine/rewards_engine.h"
namespace brave_rewards::internal {
namespace {
constexpr char kTableName[] = "publisher_prefix_list";
constexpr size_t kHashPrefixSize = 4;
constexpr size_t kMaxInsertRecords = 100'000;
std::tuple<publisher::PrefixIterator, std::string, size_t> GetPrefixInsertList(
publisher::PrefixIterator begin,
publisher::PrefixIterator end) {
DCHECK(begin != end);
size_t count = 0;
std::string values;
publisher::PrefixIterator iter = begin;
for (iter = begin; iter != end && count < kMaxInsertRecords;
++count, ++iter) {
auto prefix = *iter;
DCHECK(prefix.size() >= kHashPrefixSize);
std::string hex = base::HexEncode(prefix.data(), kHashPrefixSize);
values.append(base::StringPrintf("(x'%s'),", hex.c_str()));
}
// Remove last comma
if (!values.empty()) {
values.pop_back();
}
return {iter, std::move(values), count};
}
} // namespace
namespace database {
DatabasePublisherPrefixList::DatabasePublisherPrefixList(RewardsEngine& engine)
: DatabaseTable(engine) {}
DatabasePublisherPrefixList::~DatabasePublisherPrefixList() = default;
void DatabasePublisherPrefixList::Search(
const std::string& publisher_key,
SearchPublisherPrefixListCallback callback) {
std::string hex =
publisher::GetHashPrefixInHex(publisher_key, kHashPrefixSize);
auto command = mojom::DBCommand::New();
command->type = mojom::DBCommand::Type::READ;
command->command = base::StringPrintf(
"SELECT EXISTS(SELECT hash_prefix FROM %s WHERE hash_prefix = x'%s')",
kTableName, hex.c_str());
command->record_bindings = {mojom::DBCommand::RecordBindingType::BOOL_TYPE};
auto transaction = mojom::DBTransaction::New();
transaction->commands.push_back(std::move(command));
engine_->client()->RunDBTransaction(
std::move(transaction),
base::BindOnce(&DatabasePublisherPrefixList::OnSearch,
base::Unretained(this), std::move(callback)));
}
void DatabasePublisherPrefixList::OnSearch(
SearchPublisherPrefixListCallback callback,
mojom::DBCommandResponsePtr response) {
if (!response || !response->result ||
response->status != mojom::DBCommandResponse::Status::RESPONSE_OK ||
response->result->get_records().empty()) {
engine_->LogError(FROM_HERE)
<< "Unexpected database result while searching publisher prefix list";
std::move(callback).Run(false);
return;
}
std::move(callback).Run(
GetBoolColumn(response->result->get_records()[0].get(), 0));
}
void DatabasePublisherPrefixList::Reset(publisher::PrefixListReader reader,
ResultCallback callback) {
if (reader_) {
engine_->Log(FROM_HERE) << "Publisher prefix list batch insert in progress";
std::move(callback).Run(mojom::Result::FAILED);
return;
}
if (reader.empty()) {
engine_->LogError(FROM_HERE)
<< "Cannot reset with an empty publisher prefix list";
std::move(callback).Run(mojom::Result::FAILED);
return;
}
reader_ = std::move(reader);
InsertNext(reader_->begin(), std::move(callback));
}
void DatabasePublisherPrefixList::InsertNext(publisher::PrefixIterator begin,
ResultCallback callback) {
DCHECK(reader_ && begin != reader_->end());
auto transaction = mojom::DBTransaction::New();
if (begin == reader_->begin()) {
engine_->Log(FROM_HERE) << "Clearing publisher prefixes table";
auto command = mojom::DBCommand::New();
command->type = mojom::DBCommand::Type::RUN;
command->command = base::StringPrintf("DELETE FROM %s", kTableName);
transaction->commands.push_back(std::move(command));
}
auto insert_tuple = GetPrefixInsertList(begin, reader_->end());
engine_->Log(FROM_HERE) << "Inserting " << std::get<size_t>(insert_tuple)
<< " records into publisher prefix table";
auto command = mojom::DBCommand::New();
command->type = mojom::DBCommand::Type::RUN;
command->command = base::StringPrintf(
"INSERT OR REPLACE INTO %s (hash_prefix) VALUES %s", kTableName,
std::get<std::string>(insert_tuple).data());
transaction->commands.push_back(std::move(command));
engine_->client()->RunDBTransaction(
std::move(transaction),
base::BindOnce(&DatabasePublisherPrefixList::OnInsertNext,
base::Unretained(this), std::move(callback),
std::get<publisher::PrefixIterator>(insert_tuple)));
}
void DatabasePublisherPrefixList::OnInsertNext(
ResultCallback callback,
publisher::PrefixIterator iter,
mojom::DBCommandResponsePtr response) {
if (!response ||
response->status != mojom::DBCommandResponse::Status::RESPONSE_OK) {
reader_ = std::nullopt;
std::move(callback).Run(mojom::Result::FAILED);
return;
}
if (iter == reader_->end()) {
reader_ = std::nullopt;
std::move(callback).Run(mojom::Result::OK);
return;
}
InsertNext(iter, std::move(callback));
}
} // namespace database
} // namespace brave_rewards::internal
@@ -1,46 +0,0 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_DATABASE_PUBLISHER_PREFIX_LIST_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_DATABASE_PUBLISHER_PREFIX_LIST_H_
#include <optional>
#include <string>
#include "brave/components/brave_rewards/core/engine/database/database_table.h"
#include "brave/components/brave_rewards/core/engine/publisher/prefix_list_reader.h"
namespace brave_rewards::internal {
namespace database {
using SearchPublisherPrefixListCallback = base::OnceCallback<void(bool)>;
class DatabasePublisherPrefixList : public DatabaseTable {
public:
explicit DatabasePublisherPrefixList(RewardsEngine& engine);
~DatabasePublisherPrefixList() override;
void Reset(publisher::PrefixListReader reader, ResultCallback callback);
void Search(const std::string& publisher_key,
SearchPublisherPrefixListCallback callback);
private:
void InsertNext(publisher::PrefixIterator begin, ResultCallback callback);
void OnSearch(SearchPublisherPrefixListCallback callback,
mojom::DBCommandResponsePtr response);
void OnInsertNext(ResultCallback callback,
publisher::PrefixIterator iter,
mojom::DBCommandResponsePtr response);
std::optional<publisher::PrefixListReader> reader_;
};
} // namespace database
} // namespace brave_rewards::internal
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_DATABASE_PUBLISHER_PREFIX_LIST_H_
@@ -1,104 +0,0 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_rewards/core/engine/database/database_publisher_prefix_list.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/numerics/byte_conversions.h"
#include "brave/components/brave_rewards/core/engine/publisher/protos/publisher_prefix_list.pb.h"
#include "brave/components/brave_rewards/core/engine/test/rewards_engine_test.h"
namespace brave_rewards::internal {
namespace {
class Client : public TestRewardsEngineClient {
public:
void RunDBTransaction(mojom::DBTransactionPtr transaction,
RunDBTransactionCallback callback) override {
transactions_.push_back(transaction->Clone());
auto response = mojom::DBCommandResponse::New();
response->status = mojom::DBCommandResponse::Status::RESPONSE_OK;
std::move(callback).Run(std::move(response));
}
std::vector<mojom::DBTransactionPtr>& transactions() { return transactions_; }
private:
std::vector<mojom::DBTransactionPtr> transactions_;
};
} // namespace
class RewardsDatabasePublisherPrefixListTest : public RewardsEngineTest {
protected:
RewardsDatabasePublisherPrefixListTest()
: RewardsEngineTest(std::make_unique<Client>()) {}
Client& client() { return static_cast<Client&>(RewardsEngineTest::client()); }
publisher::PrefixListReader CreateReader(uint32_t prefix_count) {
publisher::PrefixListReader reader;
if (prefix_count == 0) {
return reader;
}
std::string prefixes;
prefixes.resize(prefix_count * 4);
for (uint32_t i = 0; i < prefix_count; ++i) {
base::as_writable_byte_span(prefixes).subspan(i * 4).first<4>().copy_from(
base::numerics::U32ToBigEndian(i));
}
publishers_pb::PublisherPrefixList message;
message.set_prefix_size(4);
message.set_compression_type(
publishers_pb::PublisherPrefixList::NO_COMPRESSION);
message.set_uncompressed_size(prefixes.size());
message.set_prefixes(std::move(prefixes));
std::string out;
message.SerializeToString(&out);
reader.Parse(out);
return reader;
}
};
TEST_F(RewardsDatabasePublisherPrefixListTest, Reset) {
database::DatabasePublisherPrefixList prefix_list(engine());
WaitFor<mojom::Result>([&](auto callback) {
prefix_list.Reset(CreateReader(100'001), std::move(callback));
});
auto& transactions = client().transactions();
ASSERT_EQ(transactions.size(), 2ul);
{
auto& transaction = transactions[0];
EXPECT_TRUE(transaction);
EXPECT_EQ(transaction->commands.size(), 2u);
EXPECT_EQ(transaction->commands[0]->command,
"DELETE FROM publisher_prefix_list");
EXPECT_TRUE(transaction->commands[1]->command.starts_with(
"INSERT OR REPLACE INTO publisher_prefix_list (hash_prefix) "
"VALUES (x'00000000'),(x'00000001'),(x'00000002'),"));
}
{
auto& transaction = transactions[1];
EXPECT_TRUE(transaction);
EXPECT_EQ(transaction->commands.size(), 1u);
EXPECT_EQ(transaction->commands[0]->command,
"INSERT OR REPLACE INTO publisher_prefix_list (hash_prefix) "
"VALUES (x'000186A0')");
}
}
} // namespace brave_rewards::internal
@@ -12,7 +12,7 @@
namespace {
constexpr int kCurrentVersionNumber = 40;
constexpr int kCurrentVersionNumber = 41;
constexpr int kCompatibleVersionNumber = 1;
} // namespace
@@ -0,0 +1,19 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_MIGRATION_MIGRATION_V41_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_MIGRATION_MIGRATION_V41_H_
namespace brave_rewards::internal::database::migration {
// Migration 41 removes the publisher prefix list table after storage was moved
// to a separate file.
inline constexpr char v41[] = R"sql(
DROP TABLE IF EXISTS publisher_prefix_list;
)sql";
} // namespace brave_rewards::internal::database::migration
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_DATABASE_MIGRATION_MIGRATION_V41_H_
@@ -0,0 +1,115 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_ITERATOR_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_ITERATOR_H_
#include <iterator>
#include <string_view>
namespace brave_rewards::internal {
// A random-access iterator over prefixes stored in an uncompressed prefix list,
// suitable for binary search.
class HashPrefixIterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = std::string_view;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
HashPrefixIterator(std::string_view data, size_t index, size_t size)
: data_(data), index_(index), size_(size) {}
HashPrefixIterator(const HashPrefixIterator& rhs)
: data_(rhs.data_), index_(rhs.index_), size_(rhs.size_) {}
std::string_view operator*() const { return PrefixAt(index_); }
std::string_view operator[](const int& rhs) const {
return PrefixAt(index_ + rhs);
}
HashPrefixIterator& operator=(const HashPrefixIterator& rhs) {
index_ = rhs.index_;
return *this;
}
HashPrefixIterator& operator+=(const int& rhs) {
index_ += rhs;
return *this;
}
HashPrefixIterator& operator-=(const int& rhs) {
index_ -= rhs;
return *this;
}
HashPrefixIterator& operator++() {
index_++;
return *this;
}
HashPrefixIterator& operator--() {
index_--;
return *this;
}
HashPrefixIterator operator+(const HashPrefixIterator& rhs) const {
return HashPrefixIterator(data_, index_ + rhs.index_, size_);
}
difference_type operator-(const HashPrefixIterator& rhs) const {
return index_ - rhs.index_;
}
HashPrefixIterator operator+(const int& rhs) const {
return HashPrefixIterator(data_, index_ + rhs, size_);
}
HashPrefixIterator operator-(const int& rhs) const {
return HashPrefixIterator(data_, index_ - rhs, size_);
}
bool operator==(const HashPrefixIterator& rhs) const {
return index_ == rhs.index_;
}
bool operator!=(const HashPrefixIterator& rhs) const {
return index_ != rhs.index_;
}
bool operator>(const HashPrefixIterator& rhs) const {
return index_ > rhs.index_;
}
bool operator<(const HashPrefixIterator& rhs) const {
return index_ < rhs.index_;
}
bool operator>=(const HashPrefixIterator& rhs) const {
return index_ >= rhs.index_;
}
bool operator<=(const HashPrefixIterator& rhs) const {
return index_ <= rhs.index_;
}
private:
std::string_view PrefixAt(size_t index) const {
return data_.substr(index_ * size_, size_);
}
const std::string_view data_;
size_t index_;
const size_t size_;
};
} // namespace brave_rewards::internal
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_ITERATOR_H_
@@ -0,0 +1,192 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_rewards/core/engine/hash_prefix_store.h"
#include <algorithm>
#include <utility>
#include "base/containers/span.h"
#include "base/containers/span_reader.h"
#include "base/files/file.h"
#include "base/memory/raw_span.h"
#include "base/numerics/byte_conversions.h"
#include "brave/components/brave_rewards/core/engine/hash_prefix_iterator.h"
#include "crypto/sha2.h"
namespace brave_rewards::internal {
namespace {
constexpr uint32_t kFileVersion = 1;
constexpr uint32_t kMinPrefixSize = 4;
constexpr uint32_t kMaxPrefixSize = 32;
struct FileParts {
uint32_t version = 0;
uint32_t prefix_size = 0;
uint64_t prefix_count = 0;
base::raw_span<const uint8_t> prefixes;
};
bool IsValidPrefixSize(size_t prefix_size) {
return prefix_size >= kMinPrefixSize && prefix_size <= kMaxPrefixSize;
}
std::optional<size_t> GetPrefixCount(size_t byte_length, size_t prefix_size) {
if (prefix_size == 0) {
return std::nullopt;
}
if (byte_length % prefix_size != 0) {
return std::nullopt;
}
return byte_length / prefix_size;
}
bool IsValidFile(const FileParts& parts) {
if (parts.version != kFileVersion) {
return false;
}
if (!IsValidPrefixSize(parts.prefix_size)) {
return false;
}
auto count = GetPrefixCount(parts.prefixes.size(), parts.prefix_size);
if (!count || count.value() != parts.prefix_count) {
return false;
}
return true;
}
std::optional<FileParts> ParseFile(base::span<const uint8_t> bytes) {
FileParts parts;
base::SpanReader reader(bytes);
if (!reader.ReadU32LittleEndian(parts.version)) {
return std::nullopt;
}
if (!reader.ReadU32LittleEndian(parts.prefix_size)) {
return std::nullopt;
}
if (!reader.ReadU64LittleEndian(parts.prefix_count)) {
return std::nullopt;
}
parts.prefixes = reader.remaining_span();
if (!IsValidFile(parts)) {
return std::nullopt;
}
return parts;
}
} // namespace
HashPrefixStore::HashPrefixStore(base::FilePath path)
: file_path_(std::move(path)) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
HashPrefixStore::~HashPrefixStore() = default;
bool HashPrefixStore::Open() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (mapped_file_) {
DCHECK(mapped_file_->IsValid());
return true;
}
open_called_ = true;
auto mapped_file = std::make_unique<base::MemoryMappedFile>();
if (!mapped_file->Initialize(file_path_)) {
return false;
}
auto parts = ParseFile(mapped_file->bytes());
if (!parts) {
return false;
}
mapped_file_ = std::move(mapped_file);
prefixes_ = base::as_string_view(parts->prefixes);
prefix_size_ = parts->prefix_size;
return true;
}
void HashPrefixStore::Close() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
prefixes_ = {};
prefix_size_ = 0;
open_called_ = false;
mapped_file_.reset();
}
bool HashPrefixStore::UpdatePrefixes(const std::string& prefixes,
size_t prefix_size) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Close();
if (!IsValidPrefixSize(prefix_size)) {
return false;
}
auto prefix_count = GetPrefixCount(prefixes.length(), prefix_size);
if (!prefix_count) {
return false;
}
base::File file;
file.Initialize(file_path_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
return false;
}
auto encoded_version = base::U32ToLittleEndian(kFileVersion);
if (!file.WriteAtCurrentPosAndCheck(base::as_byte_span(encoded_version))) {
return false;
}
auto encoded_size =
base::U32ToLittleEndian(base::checked_cast<uint32_t>(prefix_size));
if (!file.WriteAtCurrentPosAndCheck(base::as_byte_span(encoded_size))) {
return false;
}
auto encoded_count = base::U64ToLittleEndian(
base::checked_cast<uint64_t>(prefix_count.value()));
if (!file.WriteAtCurrentPosAndCheck(base::as_byte_span(encoded_count))) {
return false;
}
if (!file.WriteAtCurrentPosAndCheck(base::as_byte_span(prefixes))) {
return false;
}
file.Close();
return true;
}
bool HashPrefixStore::ContainsPrefix(const std::string& value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!open_called_) {
Open();
}
if (prefix_size_ == 0) {
return false;
}
std::string hash = crypto::SHA256HashString(value);
hash.resize(prefix_size_);
size_t prefix_count = prefixes_.length() / prefix_size_;
return std::binary_search(
HashPrefixIterator(prefixes_, 0, prefix_size_),
HashPrefixIterator(prefixes_, prefix_count, prefix_size_), hash);
}
} // namespace brave_rewards::internal
@@ -0,0 +1,54 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_STORE_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_STORE_H_
#include <memory>
#include <string>
#include <string_view>
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/sequence_checker.h"
namespace brave_rewards::internal {
// Responsible for storage and retrieval of a sorted hash prefix list. The
// operations of this class will block the current thread on IO.
class HashPrefixStore {
public:
explicit HashPrefixStore(base::FilePath path);
~HashPrefixStore();
HashPrefixStore(const HashPrefixStore&) = delete;
HashPrefixStore& operator=(const HashPrefixStore&) = delete;
// Opens the hash prefix file, if not already open.
bool Open();
// Closes the hash prefix file, if open.
void Close();
// Updates the hash prefix file.
bool UpdatePrefixes(const std::string& prefixes, size_t prefix_size);
// Returns a value indicating if the specified value exists in the prefix
// list. Opens the file if not already open.
bool ContainsPrefix(const std::string& value);
private:
const base::FilePath file_path_;
std::unique_ptr<base::MemoryMappedFile> mapped_file_;
std::string_view prefixes_;
size_t prefix_size_ = 0;
bool open_called_ = false;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace brave_rewards::internal
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_HASH_PREFIX_STORE_H_
@@ -0,0 +1,179 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_rewards/core/engine/hash_prefix_store.h"
#include <algorithm>
#include <array>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/span_writer.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/numerics/byte_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/task_environment.h"
#include "crypto/sha2.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace brave_rewards::internal {
class RewardsHashPrefixStoreTest : public testing::Test {
public:
void SetUp() override {
Test::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
store_path_ = temp_dir_.GetPath().AppendASCII("prefixes.store");
}
std::array<uint8_t, 16u> MakeFileHeader(uint32_t version,
uint32_t prefix_size,
uint64_t prefix_count) {
std::array<uint8_t, 16u> header;
base::SpanWriter writer(base::as_writable_byte_span(header));
auto encoded_version = base::U32ToLittleEndian(version);
writer.Write(base::as_byte_span(encoded_version));
auto encoded_size = base::U32ToLittleEndian(prefix_size);
writer.Write(base::as_byte_span(encoded_size));
auto encoded_count = base::U64ToLittleEndian(prefix_count);
writer.Write(base::as_byte_span(encoded_count));
return header;
}
std::string MakePrefixString(size_t prefix_size,
std::vector<std::string_view> values) {
std::vector<std::string> hash_values;
for (auto& value : values) {
std::string hash = crypto::SHA256HashString(value);
hash.resize(prefix_size);
hash_values.push_back(std::move(hash));
}
std::sort(hash_values.begin(), hash_values.end());
std::string data;
data.reserve(hash_values.size() * prefix_size);
for (auto& value : hash_values) {
data += value;
}
return data;
}
protected:
base::ScopedTempDir temp_dir_;
base::FilePath store_path_;
base::test::TaskEnvironment task_environment_;
};
TEST_F(RewardsHashPrefixStoreTest, FileDoesNotExist) {
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
EXPECT_FALSE(store.ContainsPrefix("test-value"));
}
TEST_F(RewardsHashPrefixStoreTest, EmptyFile) {
base::WriteFile(store_path_, "");
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
EXPECT_FALSE(store.ContainsPrefix("test-value"));
}
TEST_F(RewardsHashPrefixStoreTest, InvalidFileHeader) {
base::WriteFile(store_path_, "--------");
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
EXPECT_FALSE(store.ContainsPrefix("test-value"));
}
TEST_F(RewardsHashPrefixStoreTest, InvalidFileVersion) {
auto header = MakeFileHeader(2, 4, 0);
base::WriteFile(store_path_, base::as_byte_span(header));
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
}
TEST_F(RewardsHashPrefixStoreTest, InvalidPrefixSize) {
auto header = MakeFileHeader(1, 1024, 0);
base::WriteFile(store_path_, base::as_byte_span(header));
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
}
TEST_F(RewardsHashPrefixStoreTest, InvalidPrefixCount) {
auto header = MakeFileHeader(1, 4, 1);
auto content = std::string(base::as_string_view(header)) + "?";
base::WriteFile(store_path_, content);
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
}
TEST_F(RewardsHashPrefixStoreTest, WrongPrefixCount) {
auto header = MakeFileHeader(1, 4, 0);
auto content = std::string(base::as_string_view(header)) + "????";
base::WriteFile(store_path_, content);
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
}
TEST_F(RewardsHashPrefixStoreTest, InvalidDataLength) {
auto header = MakeFileHeader(1, 4, 0);
auto content = std::string(base::as_string_view(header)) + "?";
base::WriteFile(store_path_, content);
HashPrefixStore store(store_path_);
EXPECT_FALSE(store.Open());
}
TEST_F(RewardsHashPrefixStoreTest, ZeroPrefixes) {
auto header = MakeFileHeader(1, 4, 0);
base::WriteFile(store_path_, base::as_byte_span(header));
HashPrefixStore store(store_path_);
EXPECT_TRUE(store.Open());
EXPECT_FALSE(store.ContainsPrefix("test-value"));
}
TEST_F(RewardsHashPrefixStoreTest, WithPrefixes) {
HashPrefixStore store(store_path_);
size_t prefix_size = 4;
std::string data = MakePrefixString(
prefix_size, {"test-value-1", "test-value-2", "test-value-3",
"test-value-4", "test-value-5", "test-value-6",
"test-value-7", "test-value-8", "test-value-9"});
bool updated = store.UpdatePrefixes(data, prefix_size);
EXPECT_TRUE(updated);
EXPECT_TRUE(store.ContainsPrefix("test-value-4"));
EXPECT_TRUE(store.ContainsPrefix("test-value-1"));
EXPECT_TRUE(store.ContainsPrefix("test-value-9"));
EXPECT_FALSE(store.ContainsPrefix("test-value-0"));
data = MakePrefixString(prefix_size, {"test-value-10"});
updated = store.UpdatePrefixes(data, prefix_size);
EXPECT_TRUE(updated);
EXPECT_TRUE(store.ContainsPrefix("test-value-10"));
EXPECT_FALSE(store.ContainsPrefix("test-value-1"));
}
TEST_F(RewardsHashPrefixStoreTest, UpdateEmpty) {
HashPrefixStore store(store_path_);
size_t prefix_size = 8;
bool updated = store.UpdatePrefixes("", prefix_size);
EXPECT_TRUE(updated);
EXPECT_FALSE(store.ContainsPrefix("test-value-1"));
std::string data = MakePrefixString(prefix_size, {"test-value-1"});
updated = store.UpdatePrefixes(data, prefix_size);
EXPECT_TRUE(updated);
EXPECT_TRUE(store.ContainsPrefix("test-value-1"));
}
} // namespace brave_rewards::internal
@@ -1,123 +0,0 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(https://github.com/brave/brave-browser/issues/41661): Remove this and
// convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_PUBLISHER_PREFIX_ITERATOR_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_PUBLISHER_PREFIX_ITERATOR_H_
#include <iterator>
#include <string_view>
namespace brave_rewards::internal {
namespace publisher {
// A random-access iterator over prefixes stored in an
// uncompressed prefix list, suitable for binary search
class PrefixIterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = std::string_view;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
PrefixIterator(const char* data, size_t index, size_t size)
: data_(data), index_(index), size_(size) {}
PrefixIterator(const PrefixIterator& rhs)
: data_(rhs.data_), index_(rhs.index_), size_(rhs.size_) {}
std::string_view operator*() const {
size_t offset = index_ * size_;
return std::string_view(data_ + offset, size_);
}
std::string_view operator[](const int& rhs) const {
size_t offset = (index_ + rhs) * size_;
return std::string_view(data_ + offset, size_);
}
PrefixIterator& operator=(const PrefixIterator& rhs) {
index_ = rhs.index_;
return *this;
}
PrefixIterator& operator+=(const int& rhs) {
index_ += rhs;
return *this;
}
PrefixIterator& operator-=(const int& rhs) {
index_ -= rhs;
return *this;
}
PrefixIterator& operator++() {
index_++;
return *this;
}
PrefixIterator& operator--() {
index_--;
return *this;
}
PrefixIterator operator+(const PrefixIterator& rhs) const {
return PrefixIterator(data_, index_ + rhs.index_, size_);
}
difference_type operator-(const PrefixIterator& rhs) const {
return index_ - rhs.index_;
}
PrefixIterator operator+(const int& rhs) const {
return PrefixIterator(data_, index_ + rhs, size_);
}
PrefixIterator operator-(const int& rhs) const {
return PrefixIterator(data_, index_ - rhs, size_);
}
bool operator==(const PrefixIterator& rhs) const {
return index_ == rhs.index_;
}
bool operator!=(const PrefixIterator& rhs) const {
return index_ != rhs.index_;
}
bool operator>(const PrefixIterator& rhs) const {
return index_ > rhs.index_;
}
bool operator<(const PrefixIterator& rhs) const {
return index_ < rhs.index_;
}
bool operator>=(const PrefixIterator& rhs) const {
return index_ >= rhs.index_;
}
bool operator<=(const PrefixIterator& rhs) const {
return index_ <= rhs.index_;
}
private:
const char* data_;
size_t index_;
size_t size_;
};
} // namespace publisher
} // namespace brave_rewards::internal
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_PUBLISHER_PREFIX_ITERATOR_H_
@@ -73,7 +73,7 @@ PrefixListReader::ParseError PrefixListReader::Parse(
prefix_size_ = prefix_size;
// Perform a quick sanity check that the first few prefixes are in order.
PrefixIterator iter = this->begin();
HashPrefixIterator iter = this->begin();
if (iter != this->end()) {
for (size_t i = 0; i < 5; ++i) {
auto next = iter + 1;
@@ -8,7 +8,7 @@
#include <string>
#include "brave/components/brave_rewards/core/engine/publisher/prefix_iterator.h"
#include "brave/components/brave_rewards/core/engine/hash_prefix_iterator.h"
namespace brave_rewards::internal {
namespace publisher {
@@ -42,15 +42,21 @@ class PrefixListReader {
ParseError Parse(const std::string& contents);
// Returns an iterator pointing to the first prefix in the list
PrefixIterator begin() const {
return PrefixIterator(prefixes_.data(), 0, prefix_size_);
HashPrefixIterator begin() const {
return HashPrefixIterator(prefixes_, 0, prefix_size_);
}
// Returns an iterator pointing to the past-the-end element in the list
PrefixIterator end() const {
return PrefixIterator(prefixes_.data(), size(), prefix_size_);
HashPrefixIterator end() const {
return HashPrefixIterator(prefixes_, size(), prefix_size_);
}
// Returns the hash prefix size.
size_t prefix_size() const { return prefix_size_; }
// Returns the entire raw prefix list.
const std::string& prefixes() const { return prefixes_; }
// Returns the number of prefixes in the list
size_t size() const { return prefixes_.size() / prefix_size_; }
@@ -145,7 +145,7 @@ void Publisher::SaveVisit(const std::string& publisher_key,
weak_factory_.GetWeakPtr(), publisher_key, visit_data,
duration, first_visit, window_id, std::move(callback));
engine_->database()->SearchPublisherPrefixList(
engine_->client()->CreatorPrefixStoreContains(
publisher_key, base::BindOnce(&Publisher::OnSearchPrefixListForSaveVisit,
weak_factory_.GetWeakPtr(), publisher_key,
std::move(on_server_info)));
@@ -682,7 +682,7 @@ void Publisher::OnServerPublisherInfoLoaded(
// If we don't have a record in the database for this publisher, search the
// prefix list. If the prefix list indicates that the publisher is likely
// registered, then fetch the publisher data.
engine_->database()->SearchPublisherPrefixList(
engine_->client()->CreatorPrefixStoreContains(
publisher_key,
base::BindOnce(&Publisher::OnSearchPrefixListForGetServerPublisherInfo,
weak_factory_.GetWeakPtr(), publisher_key,
@@ -90,14 +90,18 @@ void PublisherPrefixListUpdater::OnFetchCompleted(mojom::Result result,
retry_count_ = 0;
engine_->Log(FROM_HERE) << "Resetting publisher prefix list table";
engine_->database()->ResetPublisherPrefixList(
std::move(reader),
auto prefix_data = mojom::HashPrefixData::New();
prefix_data->prefixes = reader.prefixes();
prefix_data->prefix_size = reader.prefix_size();
engine_->Log(FROM_HERE) << "Resetting publisher prefix list store";
engine_->client()->UpdateCreatorPrefixStore(
std::move(prefix_data),
base::BindOnce(&PublisherPrefixListUpdater::OnPrefixListInserted,
weak_factory_.GetWeakPtr()));
}
void PublisherPrefixListUpdater::OnPrefixListInserted(mojom::Result result) {
void PublisherPrefixListUpdater::OnPrefixListInserted(bool success) {
// At this point we have received a valid response from the server
// and we've attempted to insert it into the database. Store the last
// successful fetch time for calculation of next refresh interval.
@@ -109,9 +113,8 @@ void PublisherPrefixListUpdater::OnPrefixListInserted(mojom::Result result) {
StartFetchTimer(FROM_HERE, GetAutoUpdateDelay());
}
if (result != mojom::Result::OK) {
engine_->LogError(FROM_HERE)
<< "Error updating publisher prefix list table: " << result;
if (!success) {
engine_->LogError(FROM_HERE) << "Error updating publisher prefix list";
return;
}
@@ -55,7 +55,7 @@ class PublisherPrefixListUpdater {
void OnFetchTimerElapsed();
void OnFetchCompleted(mojom::Result result, std::string body);
void OnPrefixListInserted(mojom::Result result);
void OnPrefixListInserted(bool success);
base::TimeDelta GetAutoUpdateDelay();
base::TimeDelta GetRetryAfterFailureDelay();
@@ -90,7 +90,7 @@ void RefreshNext(std::unique_ptr<RefreshTaskInfo> task_info) {
// Look for publisher key in hash index.
auto& key = task_info->current->first;
task_info->engine->database()->SearchPublisherPrefixList(
task_info->engine->client()->CreatorPrefixStoreContains(
key, base::BindOnce(on_prefix_searched, std::move(task_info)));
}
@@ -12,7 +12,7 @@
namespace {
constexpr int kCurrentVersionNumber = 14;
constexpr int kCurrentVersionNumber = 15;
} // namespace
@@ -31,7 +31,8 @@ StateMigration::StateMigration(RewardsEngine& engine)
v11_(engine),
v12_(engine),
v13_(engine),
v14_(engine) {}
v14_(engine),
v15_(engine) {}
StateMigration::~StateMigration() = default;
@@ -129,6 +130,10 @@ void StateMigration::Migrate(ResultCallback callback) {
v14_.Migrate(std::move(migrate_callback));
return;
}
case 15: {
v15_.Migrate(std::move(migrate_callback));
return;
}
}
engine_->LogError(FROM_HERE)
@@ -14,6 +14,7 @@
#include "brave/components/brave_rewards/core/engine/state/state_migration_v12.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v13.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v14.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v15.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v2.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v3.h"
#include "brave/components/brave_rewards/core/engine/state/state_migration_v4.h"
@@ -57,6 +58,7 @@ class StateMigration {
StateMigrationV12 v12_;
StateMigrationV13 v13_;
StateMigrationV14 v14_;
StateMigrationV15 v15_;
};
} // namespace state
@@ -0,0 +1,24 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "brave/components/brave_rewards/core/engine/state/state_migration_v15.h"
#include <utility>
#include "brave/components/brave_rewards/core/engine/rewards_engine.h"
#include "brave/components/brave_rewards/core/engine/state/state_keys.h"
namespace brave_rewards::internal::state {
StateMigrationV15::StateMigrationV15(RewardsEngine& engine) : engine_(engine) {}
StateMigrationV15::~StateMigrationV15() = default;
void StateMigrationV15::Migrate(ResultCallback callback) {
engine_->SetState<uint64_t>(kServerPublisherListStamp, 0);
std::move(callback).Run(mojom::Result::OK);
}
} // namespace brave_rewards::internal::state
@@ -0,0 +1,33 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_STATE_STATE_MIGRATION_V15_H_
#define BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_STATE_STATE_MIGRATION_V15_H_
#include "base/memory/raw_ref.h"
#include "brave/components/brave_rewards/core/engine/rewards_callbacks.h"
namespace brave_rewards::internal {
class RewardsEngine;
namespace state {
// Migration 15 resets the `kServerPublisherListStamp` pref in order to trigger
// a download of the creator hash prefix list.
class StateMigrationV15 {
public:
explicit StateMigrationV15(RewardsEngine& engine);
~StateMigrationV15();
void Migrate(ResultCallback);
private:
const raw_ref<RewardsEngine> engine_;
};
} // namespace state
} // namespace brave_rewards::internal
#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_CORE_ENGINE_STATE_STATE_MIGRATION_V15_H_
@@ -16,7 +16,6 @@ source_set("test") {
"//brave/components/brave_rewards/core/engine/database/database_migration_unittest.cc",
"//brave/components/brave_rewards/core/engine/database/database_mock.cc",
"//brave/components/brave_rewards/core/engine/database/database_mock.h",
"//brave/components/brave_rewards/core/engine/database/database_publisher_prefix_list_unittest.cc",
"//brave/components/brave_rewards/core/engine/database/database_util_unittest.cc",
"//brave/components/brave_rewards/core/engine/endpoint/bitflyer/get_balance/get_balance_bitflyer_unittest.cc",
"//brave/components/brave_rewards/core/engine/endpoint/bitflyer/post_oauth/post_oauth_bitflyer_unittest.cc",
@@ -47,6 +46,7 @@ source_set("test") {
"//brave/components/brave_rewards/core/engine/endpoints/uphold/post_oauth_uphold_unittest.cc",
"//brave/components/brave_rewards/core/engine/endpoints/zebpay/get_balance_zebpay_unittest.cc",
"//brave/components/brave_rewards/core/engine/endpoints/zebpay/post_oauth_zebpay_unittest.cc",
"//brave/components/brave_rewards/core/engine/hash_prefix_store_unittest.cc",
"//brave/components/brave_rewards/core/engine/legacy/bat_util_unittest.cc",
"//brave/components/brave_rewards/core/engine/legacy/client_state_unittest.cc",
"//brave/components/brave_rewards/core/engine/legacy/publisher_settings_state_unittest.cc",
@@ -28,7 +28,6 @@ index|sqlite_autoindex_media_publisher_info_1|media_publisher_info|
index|sqlite_autoindex_meta_1|meta|
index|sqlite_autoindex_promotion_1|promotion|
index|sqlite_autoindex_publisher_info_1|publisher_info|
index|sqlite_autoindex_publisher_prefix_list_1|publisher_prefix_list|
index|sqlite_autoindex_recurring_donation_1|recurring_donation|
index|sqlite_autoindex_server_publisher_banner_1|server_publisher_banner|
index|sqlite_autoindex_server_publisher_info_1|server_publisher_info|
@@ -52,7 +51,6 @@ table|media_publisher_info|media_publisher_info|CREATE TABLE media_publisher_inf
table|meta|meta|CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR)
table|promotion|promotion|CREATE TABLE promotion ( promotion_id TEXT NOT NULL, version INTEGER NOT NULL, type INTEGER NOT NULL, public_keys TEXT NOT NULL, suggestions INTEGER NOT NULL DEFAULT 0, approximate_value DOUBLE NOT NULL DEFAULT 0, status INTEGER NOT NULL DEFAULT 0, expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, claimed_at TIMESTAMP, claim_id TEXT, legacy BOOLEAN DEFAULT 0 NOT NULL, claimable_until INTEGER, PRIMARY KEY (promotion_id) )
table|publisher_info|publisher_info|CREATE TABLE publisher_info ( publisher_id LONGVARCHAR PRIMARY KEY NOT NULL UNIQUE, excluded INTEGER DEFAULT 0 NOT NULL, name TEXT NOT NULL, favIcon TEXT NOT NULL, url TEXT NOT NULL, provider TEXT NOT NULL )
table|publisher_prefix_list|publisher_prefix_list|CREATE TABLE publisher_prefix_list (hash_prefix BLOB PRIMARY KEY NOT NULL)
table|recurring_donation|recurring_donation|CREATE TABLE recurring_donation ( publisher_id LONGVARCHAR NOT NULL PRIMARY KEY UNIQUE, amount DOUBLE DEFAULT 0 NOT NULL, added_date INTEGER DEFAULT 0 NOT NULL , next_contribution_at TIMESTAMP)
table|server_publisher_banner|server_publisher_banner|CREATE TABLE server_publisher_banner ( publisher_key LONGVARCHAR PRIMARY KEY NOT NULL UNIQUE, title TEXT, description TEXT, background TEXT, logo TEXT , web3_url TEXT)
table|server_publisher_info|server_publisher_info|CREATE TABLE server_publisher_info ( publisher_key LONGVARCHAR PRIMARY KEY NOT NULL, status INTEGER DEFAULT 0 NOT NULL, address TEXT NOT NULL, updated_at TIMESTAMP NOT NULL )
@@ -328,6 +328,18 @@ void TestRewardsEngineClient::RunDBTransaction(
std::move(callback).Run(std::move(response));
}
void TestRewardsEngineClient::UpdateCreatorPrefixStore(
mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) {
std::move(callback).Run(false);
}
void TestRewardsEngineClient::CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) {
std::move(callback).Run(false);
}
void TestRewardsEngineClient::Log(const std::string& file,
int32_t line,
int32_t verbose_level,
@@ -173,6 +173,14 @@ class TestRewardsEngineClient : public mojom::RewardsEngineClient {
void RunDBTransaction(mojom::DBTransactionPtr transaction,
RunDBTransactionCallback callback) override;
void UpdateCreatorPrefixStore(
mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) override;
void CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) override;
void Log(const std::string& file,
int32_t line,
int32_t verbose_level,
@@ -71,3 +71,12 @@ struct DBCommandResponse {
DBCommandResult? result;
Status status;
};
interface RewardsDatabase {
RunDBTransaction(DBTransaction transaction) => (DBCommandResponse response);
};
struct HashPrefixData {
string prefixes;
uint32 prefix_size;
};
@@ -207,6 +207,10 @@ interface RewardsEngineClient {
RunDBTransaction(DBTransaction transaction) => (DBCommandResponse response);
UpdateCreatorPrefixStore(HashPrefixData prefix_data) => (bool success);
CreatorPrefixStoreContains(string value) => (bool contains_prefix);
Log(string file, int32 line, int32 verbose_level, string message);
ClearAllNotifications();
@@ -24,6 +24,7 @@
#include "base/values.h"
#include "brave/build/ios/mojom/cpp_transformations.h"
#include "brave/components/brave_rewards/core/engine/global_constants.h"
#include "brave/components/brave_rewards/core/engine/hash_prefix_store.h"
#include "brave/components/brave_rewards/core/engine/rewards_database.h"
#include "brave/components/brave_rewards/core/engine/rewards_engine.h"
#include "brave/components/brave_rewards/core/rewards_flags.h"
@@ -42,6 +43,8 @@
#error "This file requires ARC support."
#endif
using brave_rewards::internal::HashPrefixStore;
namespace brave_rewards::internal {
template <typename T>
@@ -104,6 +107,7 @@ static NSString* const kTransferFeesPrefKey = @"transfer_fees";
brave_rewards::internal::task_deleter<RewardsClientIOS>>
_rewardsClient;
base::SequenceBound<brave_rewards::internal::RewardsDatabase> rewardsDatabase;
base::SequenceBound<HashPrefixStore> _creatorPrefixStore;
scoped_refptr<base::SequencedTaskRunner> databaseQueue;
scoped_refptr<base::SequencedTaskRunner> _engineTaskRunner;
}
@@ -172,6 +176,10 @@ static NSString* const kTransferFeesPrefKey = @"transfer_fees";
base::SequenceBound<brave_rewards::internal::RewardsDatabase>(
databaseQueue, base::FilePath(dbPath));
_creatorPrefixStore = base::SequenceBound<HashPrefixStore>(
databaseQueue,
base::FilePath(base::SysNSStringToUTF8([self creatorPrefixStorePath])));
_engineTaskRunner->PostTask(
FROM_HERE, base::BindOnce(^{
self->_rewardsClient =
@@ -266,6 +274,11 @@ static NSString* const kTransferFeesPrefKey = @"transfer_fees";
return [self.storagePath stringByAppendingPathComponent:@"Rewards.db"];
}
- (NSString*)creatorPrefixStorePath {
return
[self.storagePath stringByAppendingPathComponent:@"RewardsCreators.db"];
}
- (void)resetRewardsDatabase {
const auto dbPath = [self rewardsDatabasePath];
[NSFileManager.defaultManager removeItemAtPath:dbPath error:nil];
@@ -275,6 +288,11 @@ static NSString* const kTransferFeesPrefKey = @"transfer_fees";
rewardsDatabase =
base::SequenceBound<brave_rewards::internal::RewardsDatabase>(
databaseQueue, base::FilePath(base::SysNSStringToUTF8(dbPath)));
const auto prefixPath = [self creatorPrefixStorePath];
[NSFileManager.defaultManager removeItemAtPath:prefixPath error:nil];
_creatorPrefixStore = base::SequenceBound<HashPrefixStore>(
databaseQueue, base::FilePath(base::SysNSStringToUTF8(prefixPath)));
}
- (NSString*)randomStatePath {
@@ -1024,6 +1042,25 @@ static NSString* const kTransferFeesPrefKey = @"transfer_fees";
std::move(callback)));
}
- (void)updateCreatorPrefixStore:
(brave_rewards::mojom::HashPrefixDataPtr)prefix_data
callback:
(brave_rewards::mojom::RewardsEngineClient::
UpdateCreatorPrefixStoreCallback)callback {
_creatorPrefixStore.AsyncCall(&HashPrefixStore::UpdatePrefixes)
.WithArgs(std::move(prefix_data->prefixes), prefix_data->prefix_size)
.Then(std::move(callback));
}
- (void)creatorPrefixStoreContains:(const std::string&)value
callback:
(brave_rewards::mojom::RewardsEngineClient::
CreatorPrefixStoreContainsCallback)callback {
_creatorPrefixStore.AsyncCall(&HashPrefixStore::ContainsPrefix)
.WithArgs(value)
.Then(std::move(callback));
}
- (void)walletDisconnected:(const std::string&)wallet_type {
// Not used on iOS
}
@@ -139,6 +139,14 @@ NS_ASSUME_NONNULL_BEGIN
- (void)runDbTransaction:(brave_rewards::mojom::DBTransactionPtr)transaction
callback:(brave_rewards::mojom::RewardsEngineClient::
RunDBTransactionCallback)callback;
- (void)updateCreatorPrefixStore:
(brave_rewards::mojom::HashPrefixDataPtr)prefix_data
callback:(brave_rewards::mojom::RewardsEngineClient::
UpdateCreatorPrefixStoreCallback)callback;
- (void)creatorPrefixStoreContains:(const std::string&)value
callback:
(brave_rewards::mojom::RewardsEngineClient::
CreatorPrefixStoreContainsCallback)callback;
- (void)log:(const std::string&)file
line:(int32_t)line
verboseLevel:(int32_t)verboseLevel
@@ -106,6 +106,12 @@ class RewardsClientIOS : public brave_rewards::mojom::RewardsEngineClient {
void ReconcileStampReset() override;
void RunDBTransaction(brave_rewards::mojom::DBTransactionPtr transaction,
RunDBTransactionCallback callback) override;
void UpdateCreatorPrefixStore(
brave_rewards::mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) override;
void CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) override;
void ClearAllNotifications() override;
void ExternalWalletConnected() override;
void ExternalWalletLoggedOut() override;
@@ -180,6 +180,17 @@ void RewardsClientIOS::RunDBTransaction(
[bridge_ runDbTransaction:std::move(transaction)
callback:std::move(callback)];
}
void RewardsClientIOS::UpdateCreatorPrefixStore(
brave_rewards::mojom::HashPrefixDataPtr prefix_data,
UpdateCreatorPrefixStoreCallback callback) {
[bridge_ updateCreatorPrefixStore:std::move(prefix_data)
callback:std::move(callback)];
}
void RewardsClientIOS::CreatorPrefixStoreContains(
const std::string& value,
CreatorPrefixStoreContainsCallback callback) {
[bridge_ creatorPrefixStoreContains:value callback:std::move(callback)];
}
void RewardsClientIOS::ClearAllNotifications() {
[bridge_ clearAllNotifications];
}