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] Fetch consensus branch id from the backend #27089

Merged
merged 2 commits into from
Dec 24, 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
62 changes: 62 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_rpc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,23 @@ const GURL MakeGetLatestBlockHeightURL(const GURL& base_url) {
return base_url.ReplaceComponents(replacements);
}

const GURL MakeGetLightdInfoURL(const GURL& base_url) {
if (!base_url.is_valid()) {
return GURL();
}
if (!UrlPathEndsWithSlash(base_url)) {
return GURL();
}

GURL::Replacements replacements;
std::string path =
base::StrCat({base_url.path(),
"cash.z.wallet.sdk.rpc.CompactTxStreamer/GetLightdInfo"});
replacements.SetPathStr(path);

return base_url.ReplaceComponents(replacements);
}

const GURL MakeGetTransactionURL(const GURL& base_url) {
if (!base_url.is_valid()) {
return GURL();
Expand Down Expand Up @@ -285,6 +302,11 @@ std::string MakeGetLatestBlockHeightParams() {
return GetPrefixedProtobuf(request.SerializeAsString());
}

std::string MakeGetLightdInfoParams() {
::zcash::Empty request;
return GetPrefixedProtobuf(request.SerializeAsString());
}

std::string MakeGetTransactionParams(const std::string& tx_hash) {
::zcash::TxFilter request;
std::string as_bytes;
Expand Down Expand Up @@ -517,6 +539,28 @@ void ZCashRpc::GetCompactBlocks(const std::string& chain_id,
(*it)->DownloadAsStream(url_loader_factory_.get(), handler_it->get());
}

void ZCashRpc::GetLightdInfo(const std::string& chain_id,
GetLightdInfoCallback callback) {
GURL request_url = MakeGetLightdInfoURL(GetNetworkURL(chain_id));

if (!request_url.is_valid()) {
std::move(callback).Run(
base::unexpected(l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)));
return;
}

auto url_loader = MakeGRPCLoader(request_url, MakeGetLightdInfoParams());

UrlLoadersList::iterator it = url_loaders_list_.insert(
url_loaders_list_.begin(), std::move(url_loader));

(*it)->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&ZCashRpc::OnGetLightdInfoResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), it),
kMaxBodySize);
}

void ZCashRpc::OnGetCompactBlocksResponse(
ZCashRpc::GetCompactBlocksCallback callback,
UrlLoadersList::iterator it,
Expand Down Expand Up @@ -751,6 +795,24 @@ void ZCashRpc::OnGetAddressTxResponse(
std::move(callback).Run(result.value());
}

void ZCashRpc::OnGetLightdInfoResponse(
GetLightdInfoCallback callback,
UrlLoadersList::iterator it,
std::unique_ptr<std::string> response_body) {
url_loaders_list_.erase(it);

if (!response_body) {
std::move(callback).Run(
base::unexpected(l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)));
return;
}

GetDecoder()->ParseLightdInfo(
*response_body,
base::BindOnce(&ZCashRpc::OnParseResult<zcash::mojom::LightdInfoPtr>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}

mojo::AssociatedRemote<zcash::mojom::ZCashDecoder>& ZCashRpc::GetDecoder() {
if (zcash_decoder_.is_bound()) {
return zcash_decoder_;
Expand Down
11 changes: 11 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ class ZCashRpc {
base::expected<zcash::mojom::TreeStatePtr, std::string>)>;
using GetCompactBlocksCallback = base::OnceCallback<void(
base::expected<std::vector<zcash::mojom::CompactBlockPtr>, std::string>)>;
using GetSubtreeRootsCallback = base::OnceCallback<void(
base::expected<std::vector<zcash::mojom::SubtreeRootPtr>, std::string>)>;
using GetLightdInfoCallback = base::OnceCallback<void(
base::expected<zcash::mojom::LightdInfoPtr, std::string>)>;

ZCashRpc(NetworkManager* network_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
Expand Down Expand Up @@ -81,6 +85,9 @@ class ZCashRpc {
uint32_t to,
GetCompactBlocksCallback callback);

virtual void GetLightdInfo(const std::string& chain_id,
GetLightdInfoCallback callback);

private:
friend class base::RefCountedThreadSafe<ZCashRpc>;

Expand Down Expand Up @@ -119,6 +126,10 @@ class ZCashRpc {
StreamHandlersList::iterator handler_it,
base::expected<std::vector<std::string>, std::string> result);

void OnGetLightdInfoResponse(GetLightdInfoCallback callback,
UrlLoadersList::iterator it,
std::unique_ptr<std::string> response_body);

template <typename T>
void OnParseResult(base::OnceCallback<void(base::expected<T, std::string>)>,
T value);
Expand Down
24 changes: 15 additions & 9 deletions components/brave_wallet/browser/zcash/zcash_serializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <utility>

#include "base/containers/span.h"
#include "base/containers/span_writer.h"
#include "base/numerics/byte_conversions.h"
#include "brave/components/brave_wallet/common/btc_like_serializer_stream.h"
#include "brave/components/brave_wallet/common/hex_utils.h"
Expand All @@ -25,10 +26,7 @@ constexpr char kSaplingHashPersonalizer[] = "ZTxIdSaplingHash";
constexpr char kOrchardHashPersonalizer[] = "ZTxIdOrchardHash";

// https://zips.z.cash/zip-0244#txid-digest-1
constexpr uint32_t kConsensusBranchId = 0xC2D6D0B4;
constexpr char kTxHashPersonalizer[] =
"ZcashTxHash_"
"\xB4\xD0\xD6\xC2";
constexpr char kTxHashPersonalizerPrefix[] = "ZcashTxHash_";

constexpr uint32_t kV5TxVersion = 5 | 1 << 31 /* overwintered bit */;
// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
Expand Down Expand Up @@ -56,7 +54,7 @@ std::array<uint8_t, kZCashDigestSize> blake2b256(
void PushHeader(const ZCashTransaction& tx, BtcLikeSerializerStream& stream) {
stream.Push32(kV5TxVersion);
stream.Push32(kV5VersionGroupId);
stream.Push32(kConsensusBranchId);
stream.Push32(tx.consensus_brach_id());
stream.Push32(tx.locktime());
stream.Push32(tx.expiry_height());
}
Expand Down Expand Up @@ -93,6 +91,16 @@ std::array<uint8_t, 32> HashScriptPubKeys(const ZCashTransaction& tx) {
return blake2b256(data, base::byte_span_from_cstring("ZTxTrScriptsHash"));
}

std::array<uint8_t, kBlake2bPersonalizationSize> GetHashPersonalizer(
const ZCashTransaction& tx) {
std::array<uint8_t, kBlake2bPersonalizationSize> result;
auto span_writer = base::SpanWriter(base::span(result));
span_writer.Write(base::byte_span_from_cstring(kTxHashPersonalizerPrefix));
span_writer.WriteU32LittleEndian(tx.consensus_brach_id());
DCHECK_EQ(span_writer.remaining(), 0u);
return result;
}

} // namespace

// static
Expand Down Expand Up @@ -260,8 +268,7 @@ std::array<uint8_t, kZCashDigestSize> ZCashSerializer::CalculateTxIdDigest(
stream.PushBytes(sapling_hash);
stream.PushBytes(orchard_hash);

digest_hash =
blake2b256(data, base::byte_span_from_cstring(kTxHashPersonalizer));
digest_hash = blake2b256(data, GetHashPersonalizer(zcash_transaction));
}

std::reverse(digest_hash.begin(), digest_hash.end());
Expand Down Expand Up @@ -315,8 +322,7 @@ std::array<uint8_t, kZCashDigestSize> ZCashSerializer::CalculateSignatureDigest(
stream.PushBytes(sapling_hash);
stream.PushBytes(orchard_hash);

digest_hash =
blake2b256(data, base::byte_span_from_cstring(kTxHashPersonalizer));
digest_hash = blake2b256(data, GetHashPersonalizer(zcash_transaction));
}

return digest_hash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace brave_wallet {

TEST(ZCashSerializerTest, HashPrevouts) {
ZCashTransaction zcash_transaciton;
zcash_transaciton.set_consensus_brach_id(0xc2d6d0b4);

{
ZCashTransaction::TxInput tx_input;
Expand Down Expand Up @@ -60,6 +61,7 @@ TEST(ZCashSerializerTest, HashPrevouts) {

TEST(ZCashSerializerTest, HashOutputs) {
ZCashTransaction zcash_transaciton;
zcash_transaciton.set_consensus_brach_id(0xc2d6d0b4);

{
ZCashTransaction::TxOutput tx_output;
Expand All @@ -84,6 +86,7 @@ TEST(ZCashSerializerTest, HashOutputs) {

TEST(ZCashSerializerTest, HashSequences) {
ZCashTransaction zcash_transaciton;
zcash_transaciton.set_consensus_brach_id(0xc2d6d0b4);

{
ZCashTransaction::TxInput tx_input;
Expand All @@ -110,6 +113,7 @@ TEST(ZCashSerializerTest, HashSequences) {

TEST(ZCashSerializerTest, HashHeader) {
ZCashTransaction zcash_transaciton;
zcash_transaciton.set_consensus_brach_id(0xc2d6d0b4);
zcash_transaciton.set_expiry_height(10000);
zcash_transaciton.set_locktime(1);
EXPECT_EQ(
Expand Down Expand Up @@ -140,7 +144,7 @@ TEST(ZCashSerializerTest, HashTxIn) {
// https://zcashblockexplorer.com/transactions/360d056309669faf0d7937f41581418be5e46b04e2cea0a7b14261d7bff1d825/raw
TEST(ZCashSerializerTest, TxId_TransparentOnly) {
ZCashTransaction tx;

tx.set_consensus_brach_id(0xc2d6d0b4);
tx.set_expiry_height(2283846);
tx.set_locktime(2283826);

Expand Down Expand Up @@ -203,6 +207,7 @@ TEST(ZCashSerializerTest, OrchardBundle) {
auto key_id = mojom::ZCashKeyId::New(0, 0, 0);
auto address = keyring.GetTransparentAddress(*key_id)->address_string;
ZCashTransaction tx;
tx.set_consensus_brach_id(0xc2d6d0b4);
tx.set_expiry_height(1687144);
tx.set_locktime(0);

Expand Down
6 changes: 6 additions & 0 deletions components/brave_wallet/browser/zcash/zcash_transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ class ZCashTransaction {
expiry_height_ = expiry_height;
}

uint32_t consensus_brach_id() const { return consensus_brach_id_; }
void set_consensus_brach_id(uint32_t consensus_brach_id) {
consensus_brach_id_ = consensus_brach_id;
}

private:
TransparentPart transparent_part_;
OrchardPart orchard_part_;
Expand All @@ -169,6 +174,7 @@ class ZCashTransaction {
std::optional<OrchardMemo> memo_;
uint64_t amount_ = 0;
uint64_t fee_ = 0;
uint32_t consensus_brach_id_ = 0;
};

} // namespace brave_wallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ void ZCashTransactionCompleteManager::CompleteTransaction(
const ZCashTransaction& transaction,
const mojom::AccountIdPtr& account_id,
CompleteTransactionCallback callback) {
zcash_wallet_service_->zcash_rpc().GetLatestBlock(
zcash_wallet_service_->zcash_rpc().GetLightdInfo(
chain_id,
base::BindOnce(&ZCashTransactionCompleteManager::OnGetLatestBlockHeight,
base::BindOnce(&ZCashTransactionCompleteManager::OnGetLightdInfo,
weak_ptr_factory_.GetWeakPtr(),
Comment on lines +57 to 60
Copy link
Collaborator

Choose a reason for hiding this comment

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

How frequently branch id changes? Does it make sense to cache it per browser instance, or persist it per network?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Several times a year i guess, we have to check whether it has been changed anyway

ParamsBundle{chain_id, transaction, account_id.Clone(),
std::move(callback)}));
Expand Down Expand Up @@ -155,6 +155,31 @@ void ZCashTransactionCompleteManager::OnSignOrchardPartComplete(

#endif // BUILDFLAG(ENABLE_ORCHARD)

void ZCashTransactionCompleteManager::OnGetLightdInfo(
ParamsBundle params,
base::expected<zcash::mojom::LightdInfoPtr, std::string> result) {
if (!result.has_value()) {
std::move(params.callback).Run(base::unexpected("get lightd info error"));
return;
}

uint32_t consensus_branch_id;
if (!base::HexStringToUInt(result.value()->consensusBranchId,
&consensus_branch_id)) {
std::move(params.callback)
.Run(base::unexpected("wrong consensus branch format"));
return;
}

params.transaction.set_consensus_brach_id(consensus_branch_id);
std::string chain_id = params.chain_id;

zcash_wallet_service_->zcash_rpc().GetLatestBlock(
chain_id,
base::BindOnce(&ZCashTransactionCompleteManager::OnGetLatestBlockHeight,
weak_ptr_factory_.GetWeakPtr(), std::move(params)));
}

void ZCashTransactionCompleteManager::SignTransparentPart(ParamsBundle params) {
// Sign transparent part
if (!ZCashSerializer::SignTransparentPart(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class ZCashTransactionCompleteManager {
void OnGetLatestBlockHeight(
ParamsBundle params,
base::expected<zcash::mojom::BlockIDPtr, std::string> result);
void GetLightdInfo(ParamsBundle params);
void OnGetLightdInfo(
ParamsBundle params,
base::expected<zcash::mojom::LightdInfoPtr, std::string> result);
#if BUILDFLAG(ENABLE_ORCHARD)
void OnGetTreeState(
ParamsBundle params,
Expand All @@ -63,6 +67,9 @@ class ZCashTransactionCompleteManager {
#endif // BUILDFLAG(ENABLE_ORCHARD)

raw_ref<ZCashWalletService> zcash_wallet_service_; // Owns `this`.

std::optional<zcash::mojom::LightdInfoPtr> lightd_info_;

base::WeakPtrFactory<ZCashTransactionCompleteManager> weak_ptr_factory_{this};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ class MockZCashRPC : public ZCashRpc {
void(const std::string& chain_id,
zcash::mojom::BlockIDPtr block_id,
GetTreeStateCallback callback));

MOCK_METHOD2(GetLightdInfo,
void(const std::string& chain_id,
GetLightdInfoCallback callback));
};

} // namespace
Expand All @@ -119,13 +123,21 @@ class ZCashWalletServiceUnitTest : public testing::Test {
brave_wallet::RegisterLocalStatePrefs(local_state_.registry());
keyring_service_ =
std::make_unique<KeyringService>(nullptr, &prefs_, &local_state_);
auto zcash_rpc = std::make_unique<testing::NiceMock<MockZCashRPC>>();
zcash_wallet_service_ = std::make_unique<ZCashWalletService>(
db_path, *keyring_service_, std::move(zcash_rpc));
db_path, *keyring_service_,
std::make_unique<testing::NiceMock<MockZCashRPC>>());
GetAccountUtils().CreateWallet(kMnemonicDivideCruise, kTestWalletPassword);
zcash_account_ =
GetAccountUtils().EnsureAccount(mojom::KeyringId::kZCashMainnet, 0);
ASSERT_TRUE(zcash_account_);

ON_CALL(zcash_rpc(), GetLightdInfo(_, _))
.WillByDefault(
::testing::Invoke([&](const std::string& chain_id,
ZCashRpc::GetLightdInfoCallback callback) {
auto response = zcash::mojom::LightdInfo::New("c2d6d0b4");
std::move(callback).Run(std::move(response));
}));
}

AccountUtils GetAccountUtils() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,19 @@ struct SubtreeRoot {
uint32 complete_block_height;
};

struct LightdInfo {
string consensusBranchId;
// Other fields mentioned in https://github.com/zcash/lightwalletd/blob/1e63bee7614d8fd2be79c0ee13008f0f4aaaebbd/walletrpc/service.proto#L75C1-L93C1
// are skipped for now.
};

interface ZCashDecoder {
ParseBlockID(string data) => (BlockID? value);
ParseGetAddressUtxos(string data) => (GetAddressUtxosResponse? value);
ParseSendResponse(string data) => (SendResponse? value);
ParseRawTransaction(string data) => (RawTransaction? tx);
ParseTreeState(string data) => (TreeState? tree_state);
ParseCompactBlocks(array<string> data) => (array<CompactBlock>? compact_blocks);
ParseLightdInfo(string data) => (LightdInfo? lightd_info);
};

Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,21 @@ message CompactBlock {
repeated CompactTx vtx = 7;
ChainMetadata chainMetadata = 8;
}

message LightdInfo {
string version = 1;
string vendor = 2;
bool taddrSupport = 3; // true
string chainName = 4; // either "main" or "test"
uint64 saplingActivationHeight = 5; // depends on mainnet or testnet
string consensusBranchId =
6; // protocol identifier, see consensus/upgrades.cpp
uint64 blockHeight = 7; // latest block on the best chain
string gitCommit = 8;
string branch = 9;
string buildDate = 10;
string buildUser = 11;
uint64 estimatedHeight = 12; // less than tip height if zcashd is syncing
string zcashdBuild = 13; // example: "v4.1.1-877212414"
string zcashdSubversion = 14; // example: "/MagicBean:4.1.1/"
};
Loading
Loading