Skip to content

Commit

Permalink
Implement ZCash address discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
cypt4 committed Nov 30, 2023
1 parent 8c95bd6 commit 68326fc
Show file tree
Hide file tree
Showing 22 changed files with 1,376 additions and 352 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 @@ -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
117 changes: 99 additions & 18 deletions components/brave_wallet/browser/keyring_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2502,51 +2502,57 @@ void KeyringService::HasPendingUnlockRequest(
std::move(callback).Run(HasPendingUnlockRequest());
}

absl::optional<std::vector<std::pair<std::string, mojom::ZCashKeyIdPtr>>>
KeyringService::GetZCashAddresses(const mojom::AccountId& account_id) {
CHECK(IsZCashAccount(account_id));
absl::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 absl::nullopt;
}

// TODO(cypt4): store used addresses indexes in prefs.
auto zcash_account_info = GetZCashAccountInfo(account_id);
if (!zcash_account_info) {
return absl::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 absl::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 absl::nullopt;
}
addresses.emplace_back(*address, std::move(key_id));
addresses.emplace_back(std::move(address));
}

return addresses;
}

absl::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 absl::nullopt;
return nullptr;
}

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

void KeyringService::UpdateNextUnusedAddressForZCashAccount(
const mojom::AccountIdPtr& account_id,
absl::optional<uint32_t> next_receive_index,
absl::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 @@ -2759,6 +2797,49 @@ 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 {};
}

absl::optional<std::vector<uint8_t>> KeyringService::SignMessageByZCashKeyring(
const mojom::AccountIdPtr& account_id,
const mojom::ZCashKeyIdPtr& key_id,
Expand Down
18 changes: 12 additions & 6 deletions components/brave_wallet/browser/keyring_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,16 +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,
absl::optional<uint32_t> next_receive_index,
absl::optional<uint32_t> next_change_index);
mojom::ZCashAccountInfoPtr GetZCashAccountInfo(
const mojom::AccountIdPtr& account_id);
absl::optional<std::vector<uint8_t>> SignMessageByZCashKeyring(
const mojom::AccountIdPtr& account_id,
const mojom::ZCashKeyIdPtr& key_id,
const base::span<const uint8_t, 32> message);

absl::optional<std::vector<std::pair<std::string, mojom::ZCashKeyIdPtr>>>
GetZCashAddresses(const mojom::AccountId& account_id);
absl::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);
absl::optional<std::vector<mojom::ZCashAddressPtr>> GetZCashAddresses(
const mojom::AccountIdPtr& account_id);
absl::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
10 changes: 10 additions & 0 deletions components/brave_wallet/browser/zcash/protos/zcash_grpc_data.proto
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;
}
97 changes: 97 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,97 @@
/* 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"
#include "base/logging.h"

namespace brave_wallet {

namespace {
constexpr size_t kMaxMessageSizeBytes = 10000;
} // namespace

std::string GetPrefixedProtobuf(const std::string& serialized_proto) {
char compression = 0; // 0 means no compression
char buff[4]; // big-endian 4 bytes of message size
base::WriteBigEndian<uint32_t>(buff, serialized_proto.size());
std::string result;
result.append(&compression, 1);
result.append(buff, 4);
result.append(serialized_proto);
return result;
}

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

if (grpc_response_body.size() != size + 5) {
return absl::nullopt;
}

return grpc_response_body.substr(5);
}

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

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

bool should_resume = false;
while (!should_resume) {
if (!data_to_read_ && data_.size() > 5) {
if (data_[0] != 0) {
OnComplete(false);
return;
}
uint32_t size = 0;
base::ReadBigEndian(reinterpret_cast<const uint8_t*>(&(data_[1])), &size);
data_to_read_ = size;
if (*data_to_read_ > kMaxMessageSizeBytes) {
// Too large message
OnComplete(false);
return;
}
}

if (data_to_read_ && data_.size() > *data_to_read_) {
if (!ProcessMessage(data_.substr(0, 5 + *data_to_read_))) {
OnComplete(true);
return;
}

data_ = data_.substr(5 + *data_to_read_);
data_to_read_.reset();
} else {
should_resume = true;
}
}
std::move(resume).Run();
}

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

} // namespace brave_wallet
47 changes: 47 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,47 @@
/* 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 <string>
#include <string_view>

#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace brave_wallet {

// Resolves serialized protobuf from length-prefixed string
absl::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(const std::string& message) = 0;

absl::optional<size_t> data_to_read_;
std::string data_;
size_t retry_counter_ = 0;
};

} // namespace brave_wallet

#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_ZCASH_ZCASH_GRPC_UTILS_H_

0 comments on commit 68326fc

Please sign in to comment.