Skip to content

Commit

Permalink
Implelent shard tree and shard storage
Browse files Browse the repository at this point in the history
  • Loading branch information
cypt4 committed Nov 13, 2024
1 parent 284506d commit 6f8de17
Show file tree
Hide file tree
Showing 53 changed files with 5,628 additions and 350 deletions.
4 changes: 4 additions & 0 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,14 @@ static_library("browser") {

if (enable_orchard) {
sources += [
"zcash/orchard_shard_tree_delegate_impl.cc",
"zcash/orchard_shard_tree_delegate_impl.h",
"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_orchard_sync_state.cc",
"zcash/zcash_orchard_sync_state.h",
"zcash/zcash_shield_sync_service.cc",
"zcash/zcash_shield_sync_service.h",
]
Expand Down
10 changes: 10 additions & 0 deletions components/brave_wallet/browser/internal/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,22 @@ source_set("hd_key") {
}

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

source_set("orchard_bundle") {
sources = [
"orchard_block_scanner.cc",
"orchard_block_scanner.h",
"orchard_bundle_manager.cc",
"orchard_bundle_manager.h",
"orchard_shard_tree_manager.cc",
"orchard_shard_tree_manager.h",
]
deps = [ "//brave/components/brave_wallet/browser/zcash/rust" ]
}
Expand Down
78 changes: 44 additions & 34 deletions components/brave_wallet/browser/internal/orchard_block_scanner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@

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

#include "brave/components/brave_wallet/browser/zcash/rust/orchard_decoded_blocks_bunde.h"

namespace brave_wallet {

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

OrchardBlockScanner::Result::Result(std::vector<OrchardNote> discovered_notes,
std::vector<OrchardNullifier> 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;

Expand All @@ -29,50 +33,56 @@ OrchardBlockScanner::~OrchardBlockScanner() = default;

base::expected<OrchardBlockScanner::Result, OrchardBlockScanner::ErrorCode>
OrchardBlockScanner::ScanBlocks(
std::vector<OrchardNote> known_notes,
OrchardTreeState tree_state,
std::vector<zcash::mojom::CompactBlockPtr> blocks) {
std::vector<OrchardNullifier> found_nullifiers;
std::vector<OrchardNote> found_notes;
std::unique_ptr<orchard::OrchardDecodedBlocksBundle> result =
decoder_->ScanBlocks(tree_state, blocks);
if (!result) {
DVLOG(1) << "Failed to parse block range.";
return base::unexpected(ErrorCode::kInputError);
}

if (!result->GetDiscoveredNotes()) {
DVLOG(1) << "Failed to resolve discovered notes.";
return base::unexpected(ErrorCode::kInputError);
}

std::vector<OrchardNoteSpend> found_spends;
std::vector<OrchardNote> found_notes = result->GetDiscoveredNotes().value();

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());

OrchardNoteSpend spend;
// 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()) {
OrchardNullifier nullifier;
nullifier.block_id = block->height;
nullifier.nullifier = action_nullifier;
found_nullifiers.push_back(std::move(nullifier));
}
base::ranges::copy(orchard_action->nullifier, spend.nullifier.begin());
spend.block_id = block->height;
found_spends.push_back(std::move(spend));
}
}
}
return Result({std::move(found_notes), std::move(found_nullifiers)});

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

// static
OrchardBlockScanner::Result OrchardBlockScanner::CreateResultForTesting(
const OrchardTreeState& tree_state,
const std::vector<OrchardCommitment>& commitments) {
auto builder = orchard::OrchardDecodedBlocksBundle::CreateTestingBuilder();
for (const auto& commitment : commitments) {
builder->AddCommitment(commitment);
}
builder->SetPriorTreeState(tree_state);
return Result{{}, {}, builder->Complete()};
}

} // namespace brave_wallet
20 changes: 15 additions & 5 deletions components/brave_wallet/browser/internal/orchard_block_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
#include <vector>

#include "base/types/expected.h"
#include "brave/components/brave_wallet/browser/internal/orchard_block_scanner.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_block_decoder.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_decoded_blocks_bunde.h"
#include "brave/components/brave_wallet/common/zcash_utils.h"

namespace brave_wallet {
Expand All @@ -26,15 +28,19 @@ class OrchardBlockScanner {
struct Result {
Result();
Result(std::vector<OrchardNote> discovered_notes,
std::vector<OrchardNullifier> spent_notes);
Result(const Result&);
Result& operator=(const Result&);
std::vector<OrchardNoteSpend> spent_notes,
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
std::vector<OrchardNote> discovered_notes;
// Nullifiers for the previously discovered notes
std::vector<OrchardNullifier> spent_notes;
std::vector<OrchardNoteSpend> found_spends;
std::unique_ptr<orchard::OrchardDecodedBlocksBundle> scanned_blocks;
};

explicit OrchardBlockScanner(const OrchardFullViewKey& full_view_key);
Expand All @@ -43,9 +49,13 @@ 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,
OrchardTreeState tree_state,
std::vector<zcash::mojom::CompactBlockPtr> blocks);

static Result CreateResultForTesting(
const OrchardTreeState& tree_state,
const std::vector<OrchardCommitment>& commitments);

private:
std::unique_ptr<orchard::OrchardBlockDecoder> decoder_;
};
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 All @@ -499,9 +501,9 @@ TEST(OrchardBlockScanner, FoundKnownNullifiers) {
PrefixedHexStringToBytes(
"0x1b32edbbe4d18f28876de262518ad31122701f8c0a52e98047a337876e7eea19")
.value();
OrchardNullifier nf;
base::ranges::copy(nullifier_bytes, nf.nullifier.begin());
nf.block_id = 10;
OrchardNoteSpend spend;
base::ranges::copy(nullifier_bytes, spend.nullifier.begin());
spend.block_id = 10;

action->nullifier = nullifier_bytes;
action->ciphertext = std::vector<uint8_t>(kOrchardCipherTextSize, 0);
Expand All @@ -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], nf);
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
@@ -0,0 +1,65 @@
/* Copyright (c) 2024 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_wallet/browser/internal/orchard_shard_tree_manager.h"

#include "brave/components/brave_wallet/common/zcash_utils.h"

namespace brave_wallet {

// static
std::unique_ptr<OrchardShardTreeManager> OrchardShardTreeManager::Create(
std::unique_ptr<OrchardShardTreeDelegate> delegate) {
auto shard_tree = orchard::OrchardShardTree::Create(std::move(delegate));
if (!shard_tree) {
return nullptr;
}
return std::make_unique<OrchardShardTreeManager>(std::move(shard_tree));
}

// static
std::unique_ptr<OrchardShardTreeManager>
OrchardShardTreeManager::CreateForTesting(
std::unique_ptr<OrchardShardTreeDelegate> delegate) {
auto shard_tree =
orchard::OrchardShardTree::CreateForTesting(std::move(delegate));
if (!shard_tree) {
return nullptr;
}
return std::make_unique<OrchardShardTreeManager>(std::move(shard_tree));
}

OrchardShardTreeManager::OrchardShardTreeManager(
std::unique_ptr<::brave_wallet::orchard::OrchardShardTree> shard_tree) {
orchard_shard_tree_ = std::move(shard_tree);
}

OrchardShardTreeManager::~OrchardShardTreeManager() {}

bool OrchardShardTreeManager::InsertCommitments(
OrchardBlockScanner::Result result) {
return orchard_shard_tree_->ApplyScanResults(
std::move(result.scanned_blocks));
}

base::expected<std::vector<OrchardInput>, std::string>
OrchardShardTreeManager::CalculateWitness(std::vector<OrchardInput> notes,
uint32_t checkpoint_position) {
for (auto& input : notes) {
auto witness = orchard_shard_tree_->CalculateWitness(
input.note.orchard_commitment_tree_position, checkpoint_position);
if (!witness.has_value()) {
return base::unexpected(witness.error());
}
input.witness = witness.value();
}
return notes;
}

bool OrchardShardTreeManager::Truncate(uint32_t checkpoint) {
return orchard_shard_tree_->TruncateToCheckpoint(checkpoint);
}

} // namespace brave_wallet
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Copyright (c) 2024 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_WALLET_BROWSER_INTERNAL_ORCHARD_SHARD_TREE_MANAGER_H_
#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_INTERNAL_ORCHARD_SHARD_TREE_MANAGER_H_

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/types/expected.h"
#include "brave/components/brave_wallet/browser/internal/orchard_block_scanner.h"
#include "brave/components/brave_wallet/browser/zcash/rust/orchard_shard_tree.h"
#include "brave/components/brave_wallet/common/zcash_utils.h"

namespace brave_wallet {

class OrchardShardTreeManager {
public:
OrchardShardTreeManager(
std::unique_ptr<::brave_wallet::orchard::OrchardShardTree> shard_tree);
~OrchardShardTreeManager();
bool InsertCommitments(OrchardBlockScanner::Result commitments);
base::expected<std::vector<OrchardInput>, std::string> CalculateWitness(
std::vector<OrchardInput> notes,
uint32_t checkpoint_position);
bool Truncate(uint32_t checkpoint);
base::expected<bool, std::string> VerifyCheckpoint();

static std::unique_ptr<OrchardShardTreeManager> Create(
std::unique_ptr<OrchardShardTreeDelegate> delegate);

// Creates shard tree size of 8 for testing
static std::unique_ptr<OrchardShardTreeManager> CreateForTesting(
std::unique_ptr<OrchardShardTreeDelegate> delegate);

private:
std::unique_ptr<::brave_wallet::orchard::OrchardShardTree>
orchard_shard_tree_;
};

} // namespace brave_wallet

#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_INTERNAL_ORCHARD_SHARD_TREE_MANAGER_H_
24 changes: 24 additions & 0 deletions components/brave_wallet/browser/internal/orchard_test_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 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_wallet/browser/internal/orchard_test_utils.h"

#include "base/memory/ptr_util.h"

namespace brave_wallet {

OrchardTestUtils::OrchardTestUtils() {
orchard_test_utils_impl_ = orchard::OrchardTestUtils::Create();
}

OrchardTestUtils::~OrchardTestUtils() {}

OrchardCommitmentValue OrchardTestUtils::CreateMockCommitmentValue(
uint32_t position,
uint32_t rseed) {
return orchard_test_utils_impl_->CreateMockCommitmentValue(position, rseed);
}

} // namespace brave_wallet
Loading

0 comments on commit 6f8de17

Please sign in to comment.