Skip to content

Commit

Permalink
[ZCash] Fetch consensus branch id from the backend (#27089)
Browse files Browse the repository at this point in the history
* [ZCash] Fetch consensus branch id from the backend
Resolves brave/brave-browser#42951
  • Loading branch information
cypt4 authored Dec 24, 2024
1 parent 8d18f88 commit 1c9725d
Show file tree
Hide file tree
Showing 13 changed files with 234 additions and 14 deletions.
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(),
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

0 comments on commit 1c9725d

Please sign in to comment.