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 Zcash sync process and Orchard inputs spending #27018

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
18 changes: 16 additions & 2 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,20 @@ static_library("browser") {
"wallet_data_files_installer.cc",
"wallet_data_files_installer.h",
"wallet_data_files_installer_delegate.h",
"zcash/zcash_action_context.cc",
"zcash/zcash_action_context.h",
"zcash/zcash_block_tracker.cc",
"zcash/zcash_block_tracker.h",
"zcash/zcash_complete_transaction_task.cc",
"zcash/zcash_complete_transaction_task.h",
"zcash/zcash_create_transparent_transaction_task.cc",
"zcash/zcash_create_transparent_transaction_task.h",
"zcash/zcash_discover_next_unused_zcash_address_task.cc",
"zcash/zcash_discover_next_unused_zcash_address_task.h",
"zcash/zcash_get_transparent_utxos_context.cc",
"zcash/zcash_get_transparent_utxos_context.h",
"zcash/zcash_get_zcash_chain_tip_status_task.cc",
"zcash/zcash_get_zcash_chain_tip_status_task.h",
"zcash/zcash_grpc_utils.cc",
"zcash/zcash_grpc_utils.h",
"zcash/zcash_resolve_balance_task.cc",
Expand All @@ -242,8 +248,6 @@ static_library("browser") {
"zcash/zcash_serializer.h",
"zcash/zcash_transaction.cc",
"zcash/zcash_transaction.h",
"zcash/zcash_transaction_complete_manager.cc",
"zcash/zcash_transaction_complete_manager.h",
"zcash/zcash_transaction_utils.cc",
"zcash/zcash_transaction_utils.h",
"zcash/zcash_tx_manager.cc",
Expand Down Expand Up @@ -312,10 +316,20 @@ static_library("browser") {

if (enable_orchard) {
sources += [
"zcash/zcash_blocks_batch_scan_task.cc",
"zcash/zcash_blocks_batch_scan_task.h",
"zcash/zcash_create_shield_transaction_task.cc",
"zcash/zcash_create_shield_transaction_task.h",
"zcash/zcash_create_shielded_transaction_task.cc",
"zcash/zcash_create_shielded_transaction_task.h",
"zcash/zcash_scan_blocks_task.cc",
"zcash/zcash_scan_blocks_task.h",
"zcash/zcash_shield_sync_service.cc",
"zcash/zcash_shield_sync_service.h",
"zcash/zcash_update_subtree_roots_task.cc",
"zcash/zcash_update_subtree_roots_task.h",
"zcash/zcash_verify_chain_state_task.cc",
"zcash/zcash_verify_chain_state_task.h",
]

deps += [
Expand Down
4 changes: 4 additions & 0 deletions components/brave_wallet/browser/internal/hd_key_zip32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ OrchardFullViewKey HDKeyZip32::GetFullViewKey() {
return orchard_extended_spending_key_->GetFullViewKey();
}

OrchardSpendingKey HDKeyZip32::GetSpendingKey() {
return orchard_extended_spending_key_->GetSpendingKey();
}

} // namespace brave_wallet
2 changes: 2 additions & 0 deletions components/brave_wallet/browser/internal/hd_key_zip32.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class HDKeyZip32 {
// Full view key(fvk) is used to decode incoming transactions
OrchardFullViewKey GetFullViewKey();

OrchardSpendingKey GetSpendingKey();

private:
explicit HDKeyZip32(std::unique_ptr<orchard::OrchardExtendedSpendingKey> key);
// Extended spending key is a root key of an account, all other keys can be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ std::optional<size_t> OrchardBundleManager::random_seed_for_testing_ =
// static
std::unique_ptr<OrchardBundleManager> OrchardBundleManager::Create(
base::span<const uint8_t> tree_state,
const OrchardSpendsBundle& spends_bundle,
const std::vector<OrchardOutput>& orchard_outputs) {
if (orchard_outputs.empty()) {
return nullptr;
}
auto bundle = orchard::OrchardUnauthorizedBundle::Create(
tree_state, orchard_outputs, random_seed_for_testing_);
tree_state, spends_bundle, orchard_outputs, random_seed_for_testing_);
if (!bundle) {
return nullptr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class OrchardBundleManager {
// Returns in unauthorized state
static std::unique_ptr<OrchardBundleManager> Create(
base::span<const uint8_t> tree_state,
const OrchardSpendsBundle& spends_bundle,
const std::vector<OrchardOutput>& orchard_outputs);

static void OverrideRandomSeedForTesting(size_t seed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ TEST(OrchardBundleManagerTest, SingleOutput) {
127, 239, 163, 246, 227, 18, 158, 164, 223, 176, 169, 233, 135, 3, 166,
61, 171, 146, 149, 137, 214, 220, 81, 201, 112, 249, 53, 179}});

auto unauthorized_state =
OrchardBundleManager::Create(std::vector<uint8_t>(), std::move(outputs));
OrchardSpendsBundle orchard_spends_bundle;
auto unauthorized_state = OrchardBundleManager::Create(
std::vector<uint8_t>(), orchard_spends_bundle, std::move(outputs));
EXPECT_TRUE(unauthorized_state);
// Unauthorized state doesn't have raw tx bytes
EXPECT_FALSE(unauthorized_state->GetRawTxBytes());
Expand Down Expand Up @@ -324,8 +325,9 @@ TEST(OrchardBundleManagerTest, MultiplyOutputs) {
0x91, 0xd7, 0x34, 0xdf, 0x12, 0xd0, 0x46, 0xc9, 0x69, 0x75, 0x13,
0x30, 0xbb, 0xf4, 0x93, 0xa2, 0x41, 0xec, 0x4b, 0x88, 0xbc}});

auto unauthorized_state =
OrchardBundleManager::Create(std::vector<uint8_t>(), std::move(outputs));
OrchardSpendsBundle orchard_spends_bundle;
auto unauthorized_state = OrchardBundleManager::Create(
std::vector<uint8_t>(), orchard_spends_bundle, std::move(outputs));
EXPECT_TRUE(unauthorized_state);
// Unauthorized state doesn't have raw tx bytes
EXPECT_FALSE(unauthorized_state->GetRawTxBytes());
Expand Down Expand Up @@ -602,9 +604,11 @@ TEST(OrchardBundleManagerTest, MultiplyOutputs) {
TEST(OrchardBundleManagerTest, NoOutputs) {
OrchardBundleManager::OverrideRandomSeedForTesting(0);

OrchardSpendsBundle orchard_spends_bundle;
std::vector<OrchardOutput> outputs;
auto unauthorized_state = OrchardBundleManager::Create(
std::vector<uint8_t>(), std::vector<OrchardOutput>());
std::vector<uint8_t>(), orchard_spends_bundle,
std::vector<OrchardOutput>());
EXPECT_FALSE(unauthorized_state);
}

Expand Down
26 changes: 26 additions & 0 deletions components/brave_wallet/browser/internal/orchard_sync_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,32 @@ OrchardSyncState::CalculateWitnessForCheckpoint(
return base::ok(std::move(result));
}

base::expected<std::optional<uint32_t>, OrchardStorage::Error>
OrchardSyncState::GetLatestShardIndex(const mojom::AccountIdPtr& account_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return storage_.GetLatestShardIndex(account_id);
}

base::expected<std::optional<uint32_t>, OrchardStorage::Error>
OrchardSyncState::GetMaxCheckpointedHeight(
const mojom::AccountIdPtr& account_id,
uint32_t chain_tip_height,
uint32_t min_confirmations) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return storage_.GetMaxCheckpointedHeight(account_id, chain_tip_height,
min_confirmations);
}

base::expected<OrchardStorage::Result, OrchardStorage::Error>
OrchardSyncState::UpdateSubtreeRoots(
const mojom::AccountIdPtr& account_id,
uint32_t start_index,
const std::vector<zcash::mojom::SubtreeRootPtr>& roots) {
// DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// return storage_.UpdateSubtreeRoots(account_id, start_index, roots);
Comment on lines +158 to +159
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover it seems

return OrchardStorage::Result::kSuccess;
}

void OrchardSyncState::ResetDatabase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
storage_.ResetDatabase();
Expand Down
13 changes: 13 additions & 0 deletions components/brave_wallet/browser/internal/orchard_sync_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ class OrchardSyncState {
const uint32_t latest_scanned_block,
const std::string& latest_scanned_block_hash);

base::expected<std::optional<uint32_t>, OrchardStorage::Error>
GetLatestShardIndex(const mojom::AccountIdPtr& account_id);

base::expected<std::optional<uint32_t>, OrchardStorage::Error>
GetMaxCheckpointedHeight(const mojom::AccountIdPtr& account_id,
uint32_t chain_tip_height,
uint32_t min_confirmations);

base::expected<OrchardStorage::Result, OrchardStorage::Error>
UpdateSubtreeRoots(const mojom::AccountIdPtr& account_id,
uint32_t start_index,
const std::vector<zcash::mojom::SubtreeRootPtr>& roots);

// Clears sync data related to the account except it's birthday.
base::expected<OrchardStorage::Result, OrchardStorage::Error>
ResetAccountSyncState(const mojom::AccountIdPtr& account_id);
Expand Down
11 changes: 11 additions & 0 deletions components/brave_wallet/browser/keyring_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2212,6 +2212,17 @@ std::optional<OrchardFullViewKey> KeyringService::GetOrchardFullViewKey(

return zcash_keyring->GetOrchardFullViewKey(account_id->account_index);
}

std::optional<OrchardSpendingKey> KeyringService::GetOrchardSpendingKey(
const mojom::AccountIdPtr& account_id) {
auto* zcash_keyring = GetZCashKeyringById(account_id->keyring_id);
if (!zcash_keyring) {
return std::nullopt;
}

return zcash_keyring->GetOrchardSpendingKey(account_id->account_index);
}

#endif

void KeyringService::UpdateNextUnusedAddressForBitcoinAccount(
Expand Down
2 changes: 2 additions & 0 deletions components/brave_wallet/browser/keyring_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ class KeyringService : public mojom::KeyringService {
const mojom::ZCashKeyIdPtr& key_id);
std::optional<OrchardFullViewKey> GetOrchardFullViewKey(
const mojom::AccountIdPtr& account_id);
std::optional<OrchardSpendingKey> GetOrchardSpendingKey(
const mojom::AccountIdPtr& account_id);
#endif

const std::vector<mojom::AccountInfoPtr>& GetAllAccountInfos();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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/zcash/rust/extended_spending_key_impl.h"

#include <utility>

#include "base/memory/ptr_util.h"

namespace brave_wallet::orchard {

ExtendedSpendingKeyImpl::ExtendedSpendingKeyImpl(
absl::variant<base::PassKey<class ExtendedSpendingKey>,
base::PassKey<class ExtendedSpendingKeyImpl>>,
rust::Box<CxxOrchardExtendedSpendingKey> esk)
: extended_spending_key_(std::move(esk)) {}

ExtendedSpendingKeyImpl::~ExtendedSpendingKeyImpl() = default;

std::unique_ptr<ExtendedSpendingKey>
ExtendedSpendingKeyImpl::DeriveHardenedChild(uint32_t index) {
auto esk = extended_spending_key_->derive(index);
if (esk->is_ok()) {
return std::make_unique<ExtendedSpendingKeyImpl>(
base::PassKey<class ExtendedSpendingKeyImpl>(), esk->unwrap());
}
return nullptr;
}

std::optional<OrchardAddrRawPart>
ExtendedSpendingKeyImpl::GetDiversifiedAddress(uint32_t div_index,
OrchardAddressKind kind) {
return kind == OrchardAddressKind::External
? extended_spending_key_->external_address(div_index)
: extended_spending_key_->internal_address(div_index);
}

// static
std::unique_ptr<ExtendedSpendingKey> ExtendedSpendingKey::GenerateFromSeed(
base::span<const uint8_t> seed) {
auto mk = generate_orchard_extended_spending_key_from_seed(
rust::Slice<const uint8_t>{seed.data(), seed.size()});
if (mk->is_ok()) {
return std::make_unique<ExtendedSpendingKeyImpl>(
base::PassKey<class ExtendedSpendingKey>(), mk->unwrap());
}
return nullptr;
}

OrchardFullViewKey ExtendedSpendingKeyImpl::GetFullViewKey() {
return extended_spending_key_->full_view_key();
}

OrchardSpendingKey ExtendedSpendingKeyImpl::GetSpendingKey() {
return extended_spending_key_->spending_key();
}

} // namespace brave_wallet::orchard
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class OrchardExtendedSpendingKey {
uint32_t div_index,
OrchardAddressKind kind) = 0;

virtual OrchardSpendingKey GetSpendingKey() = 0;

virtual OrchardFullViewKey GetFullViewKey() = 0;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ OrchardFullViewKey OrchardExtendedSpendingKeyImpl::GetFullViewKey() {
return cxx_extended_spending_key_->full_view_key();
}

OrchardSpendingKey OrchardExtendedSpendingKeyImpl::GetSpendingKey() {
return cxx_extended_spending_key_->spending_key();
}

} // namespace brave_wallet::orchard
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class OrchardExtendedSpendingKeyImpl : public OrchardExtendedSpendingKey {

OrchardFullViewKey GetFullViewKey() override;

OrchardSpendingKey GetSpendingKey() override;

private:
// Extended spending key is a root key of an account, all other keys can be
// derived from esk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class OrchardUnauthorizedBundle {
// Creates OrchardUnauthorizedBundle without shielded inputs
static std::unique_ptr<OrchardUnauthorizedBundle> Create(
base::span<const uint8_t> tree_state,
const ::brave_wallet::OrchardSpendsBundle& orchard_spends,
const std::vector<::brave_wallet::OrchardOutput>& orchard_outputs,
std::optional<size_t> random_seed_for_testing);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,27 @@ OrchardUnauthorizedBundleImpl::~OrchardUnauthorizedBundleImpl() = default;
// static
std::unique_ptr<OrchardUnauthorizedBundle> OrchardUnauthorizedBundle::Create(
base::span<const uint8_t> tree_state,
const ::brave_wallet::OrchardSpendsBundle& orchard_spends,
const std::vector<::brave_wallet::OrchardOutput>& orchard_outputs,
std::optional<size_t> random_seed_for_testing) {
::rust::Vec<orchard::CxxOrchardSpend> spends;
for (const auto& input : orchard_spends.inputs) {
if (!input.witness) {
return nullptr;
}

auto& note = input.note;

orchard::CxxMerklePath merkle_path;
merkle_path.position = input.witness->position;
for (const auto& merkle_hash : input.witness->merkle_path) {
merkle_path.auth_path.push_back(orchard::CxxMerkleHash{merkle_hash});
}
spends.push_back(orchard::CxxOrchardSpend{
orchard_spends.fvk, orchard_spends.sk, note.amount, note.addr, note.rho,
note.seed, std::move(merkle_path)});
}

::rust::Vec<orchard::CxxOrchardOutput> outputs;
for (const auto& output : orchard_outputs) {
outputs.push_back(orchard::CxxOrchardOutput{
Expand All @@ -39,8 +58,7 @@ std::unique_ptr<OrchardUnauthorizedBundle> OrchardUnauthorizedBundle::Create(
CHECK_IS_TEST();
auto bundle_result = create_testing_orchard_bundle(
::rust::Slice<const uint8_t>{tree_state.data(), tree_state.size()},
::rust::Vec<::brave_wallet::orchard::CxxOrchardSpend>(),
std::move(outputs), random_seed_for_testing.value());
std::move(spends), std::move(outputs), random_seed_for_testing.value());
if (!bundle_result->is_ok()) {
return nullptr;
}
Expand All @@ -50,8 +68,7 @@ std::unique_ptr<OrchardUnauthorizedBundle> OrchardUnauthorizedBundle::Create(
} else {
auto bundle_result = create_orchard_bundle(
::rust::Slice<const uint8_t>{tree_state.data(), tree_state.size()},
::rust::Vec<::brave_wallet::orchard::CxxOrchardSpend>(),
std::move(outputs));
std::move(spends), std::move(outputs));
if (!bundle_result->is_ok()) {
return nullptr;
}
Expand Down
31 changes: 31 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_action_context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* 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/zcash/zcash_action_context.h"

namespace brave_wallet {

ZCashActionContext::ZCashActionContext(
ZCashRpc& zcash_rpc,
#if BUILDFLAG(ENABLE_ORCHARD)
base::SequenceBound<OrchardSyncState>& sync_state,
#endif // BUILDFLAG(ENABLE_ORCHARD)
const mojom::AccountIdPtr& account_id,
const std::string& chain_id)
: zcash_rpc(zcash_rpc),
#if BUILDFLAG(ENABLE_ORCHARD)
sync_state(sync_state),
#endif // BUILDFLAG(ENABLE_ORCHARD)
account_id(account_id.Clone()),
chain_id(chain_id) {
}

ZCashActionContext& ZCashActionContext::operator=(ZCashActionContext&&) =
default;
ZCashActionContext::ZCashActionContext(ZCashActionContext&&) = default;

ZCashActionContext::~ZCashActionContext() = default;

} // namespace brave_wallet
Loading
Loading