Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ZCash ] Implement shard tree and shard storage #26477

Merged
merged 2 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -314,14 +314,13 @@ static_library("browser") {
sources += [
"zcash/zcash_create_shield_transaction_task.cc",
"zcash/zcash_create_shield_transaction_task.h",
"zcash/zcash_orchard_storage.cc",
"zcash/zcash_orchard_storage.h",
"zcash/zcash_shield_sync_service.cc",
"zcash/zcash_shield_sync_service.h",
]

deps += [
"internal:orchard_bundle",
"internal/orchard_storage:orchard_storage",
"//sql",
]
}
Expand Down
21 changes: 20 additions & 1 deletion components/brave_wallet/browser/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,32 @@ source_set("hd_key") {
}

if (enable_orchard) {
source_set("test_support") {
sources = [
"orchard_test_utils.cc",
"orchard_test_utils.h",
]
public_deps = [
"//brave/components/brave_wallet/browser/zcash/rust:test_support_headers",
]
deps = [
":orchard_bundle",
"//brave/components/brave_wallet/browser/zcash/rust:test_support",
]
}

source_set("orchard_bundle") {
sources = [
"orchard_block_scanner.cc",
"orchard_block_scanner.h",
"orchard_bundle_manager.cc",
"orchard_bundle_manager.h",
"orchard_sync_state.cc",
"orchard_sync_state.h",
]
deps = [
"orchard_storage",
"//brave/components/brave_wallet/browser/zcash/rust",
]
deps = [ "//brave/components/brave_wallet/browser/zcash/rust" ]
}
}
18 changes: 9 additions & 9 deletions components/brave_wallet/browser/internal/hd_key_zip32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@
#include <utility>

#include "base/memory/ptr_util.h"
#include "brave/components/brave_wallet/browser/zcash/rust/extended_spending_key.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_extended_spending_key.h"

namespace brave_wallet {

HDKeyZip32::HDKeyZip32(std::unique_ptr<orchard::ExtendedSpendingKey> esk)
: extended_spending_key_(std::move(esk)) {}
HDKeyZip32::HDKeyZip32(std::unique_ptr<orchard::OrchardExtendedSpendingKey> esk)
: orchard_extended_spending_key_(std::move(esk)) {}

HDKeyZip32::~HDKeyZip32() = default;

// static
std::unique_ptr<HDKeyZip32> HDKeyZip32::GenerateFromSeed(
base::span<const uint8_t> seed) {
return base::WrapUnique(
new HDKeyZip32(orchard::ExtendedSpendingKey::GenerateFromSeed(seed)));
return base::WrapUnique(new HDKeyZip32(
orchard::OrchardExtendedSpendingKey::GenerateFromSeed(seed)));
}

std::unique_ptr<HDKeyZip32> HDKeyZip32::DeriveHardenedChild(uint32_t index) {
return base::WrapUnique(
new HDKeyZip32(extended_spending_key_->DeriveHardenedChild(index)));
return base::WrapUnique(new HDKeyZip32(
orchard_extended_spending_key_->DeriveHardenedChild(index)));
}

std::optional<OrchardAddrRawPart> HDKeyZip32::GetDiversifiedAddress(
uint32_t div_index,
OrchardAddressKind kind) {
return extended_spending_key_->GetDiversifiedAddress(div_index, kind);
return orchard_extended_spending_key_->GetDiversifiedAddress(div_index, kind);
}

OrchardFullViewKey HDKeyZip32::GetFullViewKey() {
return extended_spending_key_->GetFullViewKey();
return orchard_extended_spending_key_->GetFullViewKey();
}

} // namespace brave_wallet
7 changes: 4 additions & 3 deletions components/brave_wallet/browser/internal/hd_key_zip32.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace brave_wallet {

namespace orchard {
class ExtendedSpendingKey;
class OrchardExtendedSpendingKey;
} // namespace orchard

// Implements Orchard key generation from
Expand Down Expand Up @@ -43,10 +43,11 @@ class HDKeyZip32 {
OrchardFullViewKey GetFullViewKey();

private:
explicit HDKeyZip32(std::unique_ptr<orchard::ExtendedSpendingKey> key);
explicit HDKeyZip32(std::unique_ptr<orchard::OrchardExtendedSpendingKey> key);
// Extended spending key is a root key of an account, all other keys can be
// derived from esk
std::unique_ptr<orchard::ExtendedSpendingKey> extended_spending_key_;
std::unique_ptr<orchard::OrchardExtendedSpendingKey>
orchard_extended_spending_key_;
};

} // namespace brave_wallet
Expand Down
79 changes: 40 additions & 39 deletions components/brave_wallet/browser/internal/orchard_block_scanner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,75 @@

#include "brave/components/brave_wallet/browser/internal/orchard_block_scanner.h"

#include "base/threading/thread_restrictions.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_block_decoder.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_decoded_blocks_bundle.h"

namespace brave_wallet {

OrchardBlockScanner::Result::Result() = default;

OrchardBlockScanner::Result::Result(std::vector<OrchardNote> discovered_notes,
std::vector<OrchardNoteSpend> spent_notes)
OrchardBlockScanner::Result::Result(
std::vector<OrchardNote> discovered_notes,
std::vector<OrchardNoteSpend> spent_notes,
std::unique_ptr<orchard::OrchardDecodedBlocksBundle> scanned_blocks)
: discovered_notes(std::move(discovered_notes)),
spent_notes(std::move(spent_notes)) {}

OrchardBlockScanner::Result::Result(const Result&) = default;
found_spends(std::move(spent_notes)),
scanned_blocks(std::move(scanned_blocks)) {}

OrchardBlockScanner::Result::Result(OrchardBlockScanner::Result&&) = default;
OrchardBlockScanner::Result& OrchardBlockScanner::Result::operator=(
const Result&) = default;
OrchardBlockScanner::Result&&) = default;

OrchardBlockScanner::Result::~Result() = default;

OrchardBlockScanner::OrchardBlockScanner(
const OrchardFullViewKey& full_view_key)
: decoder_(orchard::OrchardBlockDecoder::FromFullViewKey(full_view_key)) {}
OrchardBlockScanner::OrchardBlockScanner(const OrchardFullViewKey& fvk)
: fvk_(fvk) {}

OrchardBlockScanner::~OrchardBlockScanner() = default;

base::expected<OrchardBlockScanner::Result, OrchardBlockScanner::ErrorCode>
OrchardBlockScanner::ScanBlocks(
std::vector<OrchardNote> known_notes,
std::vector<zcash::mojom::CompactBlockPtr> blocks) {
const OrchardTreeState& tree_state,
cdesouza-chromium marked this conversation as resolved.
Show resolved Hide resolved
const std::vector<zcash::mojom::CompactBlockPtr>& blocks) {
base::AssertLongCPUWorkAllowed();

std::unique_ptr<orchard::OrchardDecodedBlocksBundle> result =
orchard::OrchardBlockDecoder::DecodeBlocks(fvk_, tree_state, blocks);
if (!result) {
return base::unexpected(ErrorCode::kInputError);
}

std::optional<std::vector<OrchardNote>> found_notes =
result->GetDiscoveredNotes();

if (!found_notes) {
return base::unexpected(ErrorCode::kDiscoveredNotesError);
}

std::vector<OrchardNoteSpend> found_spends;
std::vector<OrchardNote> found_notes;

for (const auto& block : blocks) {
// Scan block using the decoder initialized with the provided fvk
// to find new spendable notes.
auto scan_result = decoder_->ScanBlock(block);
if (!scan_result) {
return base::unexpected(ErrorCode::kDecoderError);
}
found_notes.insert(found_notes.end(), scan_result->begin(),
scan_result->end());
// Place found notes to the known notes list so we can also check for
// nullifiers
known_notes.insert(known_notes.end(), scan_result->begin(),
scan_result->end());
for (const auto& tx : block->vtx) {
// We only scan orchard actions here
for (const auto& orchard_action : tx->orchard_actions) {
if (orchard_action->nullifier.size() != kOrchardNullifierSize) {
return base::unexpected(ErrorCode::kInputError);
}

std::array<uint8_t, kOrchardNullifierSize> action_nullifier;
base::ranges::copy(orchard_action->nullifier, action_nullifier.begin());

// Nullifier is a public information about some note being spent.
// Here we are trying to find a known spendable notes which nullifier
// matches nullifier from the processed transaction.
if (std::find_if(known_notes.begin(), known_notes.end(),
[&action_nullifier](const auto& v) {
return v.nullifier == action_nullifier;
}) != known_notes.end()) {
OrchardNoteSpend spend;
spend.block_id = block->height;
spend.nullifier = action_nullifier;
found_spends.push_back(std::move(spend));
}
// Here we are collecting nullifiers from the blocks to check them
// later.
OrchardNoteSpend spend;
base::span(spend.nullifier).copy_from(orchard_action->nullifier);
spend.block_id = block->height;
found_spends.push_back(std::move(spend));
}
}
}
return Result({std::move(found_notes), std::move(found_spends)});

return Result({std::move(found_notes.value()), std::move(found_spends),
std::move(result)});
}

} // namespace brave_wallet
29 changes: 18 additions & 11 deletions components/brave_wallet/browser/internal/orchard_block_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,36 @@
#include <vector>

#include "base/types/expected.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_block_decoder.h"
#include "brave/components/brave_wallet/browser/internal/orchard_block_scanner.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_decoded_blocks_bundle.h"
#include "brave/components/brave_wallet/common/zcash_utils.h"
#include "brave/components/services/brave_wallet/public/mojom/zcash_decoder.mojom.h"

namespace brave_wallet {

// Scans a bunch of blocks with the provided full view key to find
// spendable notes related to the account.
class OrchardBlockScanner {
public:
enum class ErrorCode { kInputError, kDecoderError };
enum class ErrorCode { kInputError, kDiscoveredNotesError, kDecoderError };

struct Result {
Result();
Result(std::vector<OrchardNote> discovered_notes,
std::vector<OrchardNoteSpend> spent_notes);
Result(const Result&);
Result& operator=(const Result&);
std::vector<OrchardNoteSpend> spent_notes,
cypt4 marked this conversation as resolved.
Show resolved Hide resolved
std::unique_ptr<orchard::OrchardDecodedBlocksBundle> scanned_blocks);
Result(const Result&) = delete;
Result& operator=(const Result&) = delete;
Result(Result&&);
Result& operator=(Result&&);
~Result();

// New notes have been discovered
// New notes have been discovered.
std::vector<OrchardNote> discovered_notes;
// Nullifiers for the previously discovered notes
std::vector<OrchardNoteSpend> spent_notes;
// Nullifiers for the previously discovered notes.
std::vector<OrchardNoteSpend> found_spends;
// Decoded blocks bundle to be insterted in the shard tree.
std::unique_ptr<orchard::OrchardDecodedBlocksBundle> scanned_blocks;
cypt4 marked this conversation as resolved.
Show resolved Hide resolved
};

explicit OrchardBlockScanner(const OrchardFullViewKey& full_view_key);
Expand All @@ -43,11 +50,11 @@ class OrchardBlockScanner {
// Scans blocks to find incoming notes related to fvk
// Also checks whether existing notes were spent.
virtual base::expected<Result, OrchardBlockScanner::ErrorCode> ScanBlocks(
std::vector<OrchardNote> known_notes,
std::vector<zcash::mojom::CompactBlockPtr> blocks);
const OrchardTreeState& tree_state,
const std::vector<zcash::mojom::CompactBlockPtr>& blocks);

private:
std::unique_ptr<orchard::OrchardBlockDecoder> decoder_;
OrchardFullViewKey fvk_;
};

} // namespace brave_wallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ TEST(OrchardBlockScannerTest, DiscoverNewNotes) {
EXPECT_EQ(result.value().discovered_notes[3].block_id, 11u);
EXPECT_EQ(result.value().discovered_notes[3].amount, 2549979667u);

EXPECT_EQ(result.value().spent_notes.size(), 0u);
EXPECT_EQ(result.value().found_spends.size(), 5u);
}

TEST(OrchardBlockScannerTest, WrongInput) {
Expand Down Expand Up @@ -469,11 +469,13 @@ TEST(OrchardBlockScanner, FoundKnownNullifiers_SameBatch) {
EXPECT_EQ(result.value().discovered_notes[0].block_id, 10u);
EXPECT_EQ(result.value().discovered_notes[0].amount, 3625561528u);

EXPECT_EQ(result.value().spent_notes.size(), 1u);
EXPECT_EQ(result.value().spent_notes[0].block_id, 11u);
EXPECT_EQ(result.value().found_spends.size(), 2u);
EXPECT_EQ(result.value().found_spends[0].block_id, 10u);
EXPECT_EQ(result.value().found_spends[1].block_id, 11u);

EXPECT_EQ(
std::vector<uint8_t>(result.value().spent_notes[0].nullifier.begin(),
result.value().spent_notes[0].nullifier.end()),
std::vector<uint8_t>(result.value().found_spends[1].nullifier.begin(),
result.value().found_spends[1].nullifier.end()),
PrefixedHexStringToBytes(
"0x6588cc7fabfab2b2a4baa89d4dfafaa50cc89d22f96d10fb7689461b921ad40d")
.value());
Expand Down Expand Up @@ -525,11 +527,13 @@ TEST(OrchardBlockScanner, FoundKnownNullifiers) {
notes.push_back(note);
blocks.push_back(std::move(block));

auto result = scanner.ScanBlocks(std::move(notes), std::move(blocks));
OrchardTreeState tree_state;

auto result = scanner.ScanBlocks(tree_state, std::move(blocks));

EXPECT_TRUE(result.has_value());
EXPECT_EQ(result.value().spent_notes.size(), 1u);
EXPECT_EQ(result.value().spent_notes[0], spend);
EXPECT_EQ(result.value().found_spends.size(), 1u);
EXPECT_EQ(result.value().found_spends[0].nullifier, spend.nullifier);
EXPECT_EQ(result.value().discovered_notes.size(), 0u);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ std::optional<size_t> OrchardBundleManager::random_seed_for_testing_ =
// static
std::unique_ptr<OrchardBundleManager> OrchardBundleManager::Create(
base::span<const uint8_t> tree_state,
const std::vector<::brave_wallet::OrchardOutput>& orchard_outputs) {
const std::vector<OrchardOutput>& orchard_outputs) {
if (orchard_outputs.empty()) {
return nullptr;
}
auto bundle = orchard::UnauthorizedOrchardBundle::Create(
auto bundle = orchard::OrchardUnauthorizedBundle::Create(
tree_state, orchard_outputs, random_seed_for_testing_);
if (!bundle) {
return nullptr;
Expand All @@ -36,11 +36,11 @@ std::unique_ptr<OrchardBundleManager> OrchardBundleManager::Create(
}

OrchardBundleManager::OrchardBundleManager(
std::unique_ptr<orchard::UnauthorizedOrchardBundle> unauthorized_bundle)
std::unique_ptr<orchard::OrchardUnauthorizedBundle> unauthorized_bundle)
: unauthorized_orchard_bundle_(std::move(unauthorized_bundle)) {}

OrchardBundleManager::OrchardBundleManager(
std::unique_ptr<orchard::AuthorizedOrchardBundle> authorized_bundle)
std::unique_ptr<orchard::OrchardAuthorizedBundle> authorized_bundle)
: authorized_orchard_bundle_(std::move(authorized_bundle)) {}

std::optional<std::array<uint8_t, kZCashDigestSize>>
Expand Down
Loading
Loading