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

Implement ZCash address discovery #21150

Merged
merged 4 commits into from
Dec 8, 2023
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
4 changes: 4 additions & 0 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ static_library("browser") {
"wallet_data_files_installer_delegate.h",
"zcash/zcash_block_tracker.cc",
"zcash/zcash_block_tracker.h",
"zcash/zcash_grpc_utils.cc",
"zcash/zcash_grpc_utils.h",
"zcash/zcash_rpc.cc",
"zcash/zcash_rpc.h",
"zcash/zcash_serializer.cc",
Expand All @@ -224,6 +226,8 @@ static_library("browser") {
"zcash/zcash_tx_state_manager.h",
"zcash/zcash_wallet_service.cc",
"zcash/zcash_wallet_service.h",
"zcash/zcash_wallet_service_tasks.cc",
"zcash/zcash_wallet_service_tasks.h",
]
if (enable_ipfs_local_node) {
sources += [
Expand Down
4 changes: 2 additions & 2 deletions components/brave_wallet/browser/brave_wallet_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2077,8 +2077,8 @@ void BraveWalletService::GenerateReceiveAddress(
std::move(callback).Run("", WalletInternalErrorMessage());
return;
}
zcash_wallet_service_->GetReceiverAddress(
std::move(account_id),
zcash_wallet_service_->RunDiscovery(
std::move(account_id), false,
base::BindOnce(&BraveWalletService::OnGenerateZecReceiveAddress,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return;
Expand Down
119 changes: 100 additions & 19 deletions components/brave_wallet/browser/keyring_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2501,51 +2501,57 @@ void KeyringService::HasPendingUnlockRequest(
std::move(callback).Run(HasPendingUnlockRequest());
}

std::optional<std::vector<std::pair<std::string, mojom::ZCashKeyIdPtr>>>
KeyringService::GetZCashAddresses(const mojom::AccountId& account_id) {
CHECK(IsZCashAccount(account_id));
std::optional<std::vector<mojom::ZCashAddressPtr>>
KeyringService::GetZCashAddresses(const mojom::AccountIdPtr& account_id) {
CHECK(account_id);
CHECK(IsZCashAccount(*account_id));

auto* zcash_keyring = GetZCashKeyringById(account_id.keyring_id);
auto* zcash_keyring = GetZCashKeyringById(account_id->keyring_id);
if (!zcash_keyring) {
return std::nullopt;
}

// TODO(cypt4): store used addresses indexes in prefs.
auto zcash_account_info = GetZCashAccountInfo(account_id);
if (!zcash_account_info) {
return std::nullopt;
}

// TODO(cypt4): temporarily just return first 5 recieve and 5 change
// addresses.
std::vector<std::pair<std::string, mojom::ZCashKeyIdPtr>> addresses;
for (auto i = 0; i < 5; ++i) {
auto key_id =
mojom::ZCashKeyId::New(account_id.bitcoin_account_index, 0, i);
std::vector<mojom::ZCashAddressPtr> addresses;
for (auto i = 0u;
i < zcash_account_info->next_transparent_receive_address->key_id->index;
++i) {
auto key_id = mojom::ZCashKeyId::New(account_id->bitcoin_account_index,
kBitcoinReceiveIndex, i);
auto address = zcash_keyring->GetAddress(*key_id);
if (!address) {
return std::nullopt;
}
addresses.emplace_back(*address, std::move(key_id));
addresses.emplace_back(std::move(address));
}
for (auto i = 0; i < 5; ++i) {
auto key_id =
mojom::ZCashKeyId::New(account_id.bitcoin_account_index, 1, i);
for (auto i = 0u;
i < zcash_account_info->next_transparent_change_address->key_id->index;
++i) {
auto key_id = mojom::ZCashKeyId::New(account_id->bitcoin_account_index,
kBitcoinChangeIndex, i);
auto address = zcash_keyring->GetAddress(*key_id);
if (!address) {
return std::nullopt;
return absl::nullopt;
}
addresses.emplace_back(*address, std::move(key_id));
addresses.emplace_back(std::move(address));
}

return addresses;
}

std::optional<std::string> KeyringService::GetZCashAddress(
mojom::ZCashAddressPtr KeyringService::GetZCashAddress(
const mojom::AccountId& account_id,
const mojom::ZCashKeyId& key_id) {
CHECK(IsZCashAccount(account_id));
CHECK_EQ(account_id.bitcoin_account_index, key_id.account);

auto* zcash_keyring = GetZCashKeyringById(account_id.keyring_id);
if (!zcash_keyring) {
return std::nullopt;
return nullptr;
}

return zcash_keyring->GetAddress(key_id);
Expand Down Expand Up @@ -2598,6 +2604,38 @@ void KeyringService::UpdateNextUnusedAddressForBitcoinAccount(
}
}

void KeyringService::UpdateNextUnusedAddressForZCashAccount(
const mojom::AccountIdPtr& account_id,
std::optional<uint32_t> next_receive_index,
std::optional<uint32_t> next_change_index) {
CHECK(account_id);
CHECK(IsZCashAccount(*account_id));
CHECK(next_receive_index || next_change_index);

ScopedDictPrefUpdate keyrings_update(profile_prefs_, kBraveWalletKeyrings);
base::Value::List& account_metas = GetListPrefForKeyringUpdate(
keyrings_update, kAccountMetas, account_id->keyring_id);
for (auto& item : account_metas) {
if (auto derived_account = DerivedAccountInfo::FromValue(item)) {
if (*account_id == *MakeAccountIdForDerivedAccount(
*derived_account, account_id->keyring_id)) {
if (next_receive_index) {
derived_account->bitcoin_next_receive_address_index =
*next_receive_index;
}
if (next_change_index) {
derived_account->bitcoin_next_change_address_index =
*next_change_index;
}

item = derived_account->ToValue();
NotifyAccountsChanged();
return;
}
}
}
}

mojom::BitcoinAccountInfoPtr KeyringService::GetBitcoinAccountInfo(
const mojom::AccountIdPtr& account_id) {
CHECK(account_id);
Expand Down Expand Up @@ -2757,6 +2795,49 @@ std::optional<std::vector<uint8_t>> KeyringService::SignMessageByBitcoinKeyring(
*key_id, message);
}

mojom::ZCashAccountInfoPtr KeyringService::GetZCashAccountInfo(
const mojom::AccountIdPtr& account_id) {
CHECK(account_id);
CHECK(IsZCashAccount(*account_id));

auto keyring_id = account_id->keyring_id;
auto* zcash_keyring = GetZCashKeyringById(keyring_id);
if (!zcash_keyring) {
return {};
}

for (const auto& derived_account_info :
GetDerivedAccountsForKeyring(profile_prefs_, keyring_id)) {
if (account_id->bitcoin_account_index !=
derived_account_info.account_index) {
continue;
}
auto result = mojom::ZCashAccountInfo::New();

auto receive_key_id = mojom::ZCashKeyId::New(
account_id->bitcoin_account_index, kBitcoinReceiveIndex,
derived_account_info.bitcoin_next_receive_address_index.value_or(0));
auto receive_address = zcash_keyring->GetAddress(*receive_key_id);
if (!receive_address) {
return {};
}
result->next_transparent_receive_address = std::move(receive_address);

auto change_key_id = mojom::ZCashKeyId::New(
account_id->bitcoin_account_index, kBitcoinChangeIndex,
derived_account_info.bitcoin_next_change_address_index.value_or(0));
auto change_address = zcash_keyring->GetAddress(*change_key_id);
if (!change_address) {
return {};
}

result->next_transparent_change_address = std::move(change_address);
return result;
}

return {};
}

std::optional<std::vector<uint8_t>> KeyringService::SignMessageByZCashKeyring(
const mojom::AccountIdPtr& account_id,
const mojom::ZCashKeyIdPtr& key_id,
Expand Down
17 changes: 12 additions & 5 deletions components/brave_wallet/browser/keyring_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,22 @@ class KeyringService : public KeyedService, public mojom::KeyringService {
const mojom::AccountIdPtr& account_id,
const mojom::BitcoinKeyIdPtr& key_id,
base::span<const uint8_t, 32> message);

/* ZCash */
void UpdateNextUnusedAddressForZCashAccount(
const mojom::AccountIdPtr& account_id,
std::optional<uint32_t> next_receive_index,
std::optional<uint32_t> next_change_index);
mojom::ZCashAccountInfoPtr GetZCashAccountInfo(
const mojom::AccountIdPtr& account_id);
std::optional<std::vector<uint8_t>> SignMessageByZCashKeyring(
const mojom::AccountIdPtr& account_id,
const mojom::ZCashKeyIdPtr& key_id,
const base::span<const uint8_t, 32> message);

std::optional<std::vector<std::pair<std::string, mojom::ZCashKeyIdPtr>>>
GetZCashAddresses(const mojom::AccountId& account_id);
std::optional<std::string> GetZCashAddress(const mojom::AccountId& account_id,
const mojom::ZCashKeyId& key_id);
mojom::ZCashAddressPtr GetZCashAddress(const mojom::AccountId& account_id,
const mojom::ZCashKeyId& key_id);
std::optional<std::vector<mojom::ZCashAddressPtr>> GetZCashAddresses(
const mojom::AccountIdPtr& account_id);
std::optional<std::vector<uint8_t>> GetZCashPubKey(
const mojom::AccountIdPtr& account_id,
const mojom::ZCashKeyIdPtr& key_id);
Expand Down
1 change: 1 addition & 0 deletions components/brave_wallet/browser/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ source_set("brave_wallet_unit_tests") {
"//brave/components/brave_wallet/browser/unstoppable_domains_dns_resolve_unittest.cc",
"//brave/components/brave_wallet/browser/unstoppable_domains_multichain_calls_unittest.cc",
"//brave/components/brave_wallet/browser/wallet_data_files_installer_unittest.cc",
"//brave/components/brave_wallet/browser/zcash/zcash_grpc_utils_unittest.cc",
"//brave/components/brave_wallet/browser/zcash/zcash_keyring_unittest.cc",
"//brave/components/brave_wallet/browser/zcash/zcash_serializer_unittest.cc",
"//brave/components/brave_wallet/browser/zcash/zcash_wallet_service_unittest.cc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,13 @@ message SendResponse {
int32 errorCode = 1;
string errorMessage = 2;
}

message BlockRange {
BlockID start = 1;
BlockID end = 2;
}

message TransparentAddressBlockFilter {
string address = 1;
BlockRange range = 2;
}
100 changes: 100 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_grpc_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Copyright (c) 2023 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_grpc_utils.h"

#include <utility>

#include "base/big_endian.h"
#include "base/functional/callback.h"

namespace brave_wallet {

namespace {
constexpr size_t kMaxMessageSizeBytes = 10000;
constexpr size_t kGrpcHeaderSize = 5;
constexpr char kNoCompression = 0;
thypon marked this conversation as resolved.
Show resolved Hide resolved
} // namespace

std::string GetPrefixedProtobuf(const std::string& serialized_proto) {
std::string result(kGrpcHeaderSize, 0);
result[0] = kNoCompression;
base::WriteBigEndian<uint32_t>(&result[1], serialized_proto.size());
result.append(serialized_proto);
return result;
}

// Resolves serialized protobuf from length-prefixed string
std::optional<std::string> ResolveSerializedMessage(
const std::string& grpc_response_body) {
if (grpc_response_body.size() < kGrpcHeaderSize) {
return std::nullopt;
}
if (grpc_response_body[0] != kNoCompression) {
// Compression is not supported yet
return std::nullopt;
}
uint32_t size = 0;
base::ReadBigEndian(
reinterpret_cast<const uint8_t*>(&(grpc_response_body[1])), &size);

if (grpc_response_body.size() != size + kGrpcHeaderSize) {
return std::nullopt;
}

return grpc_response_body.substr(kGrpcHeaderSize);
}

GRrpcMessageStreamHandler::GRrpcMessageStreamHandler() = default;
GRrpcMessageStreamHandler::~GRrpcMessageStreamHandler() = default;

void GRrpcMessageStreamHandler::OnDataReceived(std::string_view string_piece,
base::OnceClosure resume) {
data_.append(string_piece);
std::string_view data_view(data_);

bool should_resume = false;
while (!should_resume) {
std::optional<size_t> message_body_size;
if (data_view.size() > kGrpcHeaderSize) {
if (data_view[0] != kNoCompression) {
OnComplete(false);
return;
}
uint32_t size = 0;
base::ReadBigEndian(reinterpret_cast<const uint8_t*>(&(data_view[1])),
&size);
message_body_size = size;
if (*message_body_size > kMaxMessageSizeBytes) {
// Too large message
OnComplete(false);
return;
}
}

if (message_body_size &&
data_view.size() >= (kGrpcHeaderSize + *message_body_size)) {
if (!ProcessMessage(
data_view.substr(0, kGrpcHeaderSize + *message_body_size))) {
OnComplete(true);
return;
}

data_view = data_view.substr(kGrpcHeaderSize + *message_body_size);
} else {
should_resume = true;
}
}
data_ = std::string(data_view);
std::move(resume).Run();
}

void GRrpcMessageStreamHandler::OnRetry(base::OnceClosure start_retry) {
if (retry_counter_++ < 5) {
std::move(start_retry).Run();
}
}

} // namespace brave_wallet
46 changes: 46 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_grpc_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* Copyright (c) 2023 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_ZCASH_ZCASH_GRPC_UTILS_H_
#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_ZCASH_ZCASH_GRPC_UTILS_H_

#include <optional>
#include <string>
#include <string_view>

#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"

namespace brave_wallet {

// Resolves serialized protobuf from length-prefixed string
std::optional<std::string> ResolveSerializedMessage(
const std::string& grpc_response_body);

// Prefixes provided serialized protobuf with compression byte and 4 bytes of
// message size. See
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
std::string GetPrefixedProtobuf(const std::string& serialized_proto);

// Handles a stream of GRPC objects
class GRrpcMessageStreamHandler
: public network::SimpleURLLoaderStreamConsumer {
public:
GRrpcMessageStreamHandler();
~GRrpcMessageStreamHandler() override;

void OnDataReceived(std::string_view string_piece,
base::OnceClosure resume) override;
void OnRetry(base::OnceClosure start_retry) override;

private:
virtual bool ProcessMessage(std::string_view message) = 0;

std::string data_;
size_t retry_counter_ = 0;
};

} // namespace brave_wallet

#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_ZCASH_ZCASH_GRPC_UTILS_H_
Loading