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 1 commit
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
28 changes: 19 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/to_vector.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

#include "base/containers/to_vector.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,20 @@ 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;
uint32_t consensusBranchId = tx.consensus_brach_id();
base::span(result)
.subspan(0, sizeof(kTxHashPersonalizerPrefix) - 1)
.copy_from(base::byte_span_from_cstring(kTxHashPersonalizerPrefix));
base::span(result)
.subspan(sizeof(kTxHashPersonalizerPrefix) - 1, sizeof(consensusBranchId))
.copy_from(base::byte_span_from_ref(base::numerics::U32FromLittleEndian(
base::byte_span_from_ref(consensusBranchId))));
return result;
}

} // namespace

Copy link
Collaborator

@supermassive supermassive Dec 24, 2024

Choose a reason for hiding this comment

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

That should be done with SpanWriter
something like

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

// static
Expand Down Expand Up @@ -260,8 +272,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 +326,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 @@ -334,7 +334,6 @@ void ZCashWalletService::CompleteTransactionDone(
result.error());
return;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

need this change?

auto tx = ZCashSerializer::SerializeRawTransaction(result.value());
zcash_rpc_->SendTransaction(
chain_id, tx,
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);
};

Loading
Loading