[Polkadot] [Wallet] add transfer_allow_death extrinsic signing (#33318)

Resolves brave/brave-browser#51481

This PR relocates some important constants so that they can be visible to the
KeyringService (for the sake of signing binary blogs using the appropriate
keyring for the account).

We also update the PolkadotKerying class to support deterministic RNG so
signatures are consistent.

We finally add an extrinsic signature task which asynchronously fetches all the
required data from the RPC nodes and then assembles the final blob of bytes.
Tests should cover the error paths for each potentially-failed RPC call.
This commit is contained in:
Christian Mazakas
2026-02-03 07:34:54 -08:00
committed by GitHub
parent c710b10641
commit 42182b8ee8
19 changed files with 2217 additions and 26 deletions
+2
View File
@@ -187,6 +187,8 @@ static_library("browser") {
"polkadot/polkadot_block_tracker.h",
"polkadot/polkadot_extrinsic.cc",
"polkadot/polkadot_extrinsic.h",
"polkadot/polkadot_signed_transfer_task.cc",
"polkadot/polkadot_signed_transfer_task.h",
"polkadot/polkadot_substrate_rpc.cc",
"polkadot/polkadot_substrate_rpc.h",
"polkadot/polkadot_transaction.cc",
@@ -56,7 +56,10 @@ source_set("hd_key") {
"hd_key_sr25519.cc",
"hd_key_sr25519.h",
]
deps += [ "//brave/components/brave_wallet/browser/internal:sr25519_rs" ]
deps += [
"//brave/components/brave_wallet/browser/internal:sr25519_rs",
"//brave/components/brave_wallet/common:common_constants",
]
}
if (enable_orchard) {
@@ -11,19 +11,13 @@
#include <vector>
#include "base/containers/span.h"
#include "brave/components/brave_wallet/common/brave_wallet_constants.h"
#include "third_party/rust/cxx/v1/cxx.h"
namespace brave_wallet {
struct CxxSchnorrkelKeyPair;
// https://docs.rs/schnorrkel/0.11.4/schnorrkel/keys/index.html#constants
inline constexpr size_t kSr25519SeedSize = 32;
inline constexpr size_t kSr25519SecretKeySize = 64;
inline constexpr size_t kSr25519PublicKeySize = 32;
inline constexpr size_t kSr25519SignatureSize = 64;
inline constexpr size_t kSr25519Pkcs8Size = 117;
// This class implements basic sr25519 functionality, which is a scheme for
// signing and key derivation using Schnorr signatures, using Ristretto as a
// means of creating elliptic curve groups.
@@ -67,6 +67,7 @@ mod ffi {
type CxxPolkadotChainMetadataResult;
fn get_ss58_prefix(chain_metadata: &CxxPolkadotChainMetadata) -> u16;
fn clone_metadata(self: &CxxPolkadotChainMetadata) -> Box<CxxPolkadotChainMetadata>;
fn is_ok(self: &CxxPolkadotChainMetadataResult) -> bool;
fn error_message(self: &CxxPolkadotChainMetadataResult) -> String;
@@ -176,6 +177,7 @@ impl fmt::Display for Error {
}
}
#[derive(Clone, Copy)]
struct CxxPolkadotChainMetadata {
balances_pallet_index: u8,
transfer_allow_death_call_index: u8,
@@ -186,6 +188,12 @@ fn get_ss58_prefix(chain_metadata: &CxxPolkadotChainMetadata) -> u16 {
chain_metadata.ss58_prefix
}
impl CxxPolkadotChainMetadata {
fn clone_metadata(self: &CxxPolkadotChainMetadata) -> Box<CxxPolkadotChainMetadata> {
Box::new(*self)
}
}
impl_result!(CxxPolkadotChainMetadata, CxxPolkadotChainMetadataResult);
impl_result!(CxxPolkadotDecodeUnsignedTransfer, CxxPolkadotDecodeUnsignedTransferResult);
@@ -2975,6 +2975,20 @@ KeyringService::GetPolkadotPubKey(const mojom::AccountIdPtr& account_id) {
return {key};
}
std::optional<std::array<uint8_t, kSr25519SignatureSize>>
KeyringService::SignMessageByPolkadotKeyring(
const mojom::AccountIdPtr& account_id,
base::span<const uint8_t> message) {
CHECK(account_id);
auto* keyring = GetKeyring<PolkadotKeyring>(account_id->keyring_id);
if (!keyring) {
return std::nullopt;
}
return keyring->SignMessage(message, account_id->account_index);
}
void KeyringService::UpdateNextUnusedAddressForBitcoinAccount(
const mojom::AccountIdPtr& account_id,
std::optional<uint32_t> next_receive_index,
@@ -18,6 +18,7 @@
#include "brave/components/brave_wallet/browser/cardano/cardano_hd_keyring.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_utils.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
#include "brave/components/brave_wallet/common/brave_wallet_constants.h"
#include "brave/components/brave_wallet/common/brave_wallet_types.h"
#include "brave/components/brave_wallet/common/buildflags/buildflags.h"
#include "brave/components/brave_wallet/common/zcash_utils.h"
@@ -291,6 +292,10 @@ class KeyringService : public mojom::KeyringService {
std::optional<std::array<uint8_t, kPolkadotSubstrateAccountIdSize>>
GetPolkadotPubKey(const mojom::AccountIdPtr& account_id);
std::optional<std::array<uint8_t, kSr25519SignatureSize>>
SignMessageByPolkadotKeyring(const mojom::AccountIdPtr& account_id,
base::span<const uint8_t> message);
const std::vector<mojom::AccountInfoPtr>& GetAllAccountInfos();
mojom::AccountInfoPtr FindAccount(const mojom::AccountIdPtr& account_id);
mojom::AccountInfoPtr GetSelectedWalletAccount();
@@ -359,6 +364,7 @@ class KeyringService : public mojom::KeyringService {
friend class KeyringServiceUnitTest;
friend class AssetDiscoveryManagerUnitTest;
friend class SolanaTransactionUnitTest;
friend class PolkadotWalletServiceUnitTest;
void ResetAllAccountInfosCache();
mojom::AccountInfoPtr AddHDAccountForKeyring(mojom::KeyringId keyring_id,
@@ -22,6 +22,9 @@ bool IsBoxNonNull(const rust::Box<CxxPolkadotChainMetadata>& chain_metadata) {
} // namespace
PolkadotChainMetadata::PolkadotChainMetadata(const PolkadotChainMetadata& other)
: chain_metadata_(other.chain_metadata_->clone_metadata()) {}
PolkadotChainMetadata::PolkadotChainMetadata(PolkadotChainMetadata&&) noexcept =
default;
@@ -36,6 +36,7 @@ namespace brave_wallet {
class PolkadotChainMetadata {
public:
PolkadotChainMetadata() = delete;
PolkadotChainMetadata(const PolkadotChainMetadata&);
PolkadotChainMetadata(PolkadotChainMetadata&&) noexcept;
~PolkadotChainMetadata();
@@ -161,6 +161,13 @@ void PolkadotKeyring::SetRandBytesForTesting( // IN-TEST
rand_nonce_bytes_for_testing_ = nonce_bytes;
}
void PolkadotKeyring::SetSignatureRngForTesting() {
CHECK_IS_TEST();
for (auto& [idx, keypair] : secondary_keys_) {
keypair.UseMockRngForTesting(); // IN-TEST
}
}
// Creates JSON to export Polkadot account info in the proper format.
// At this time password is reused to encrypt encoded data.
std::optional<std::string> PolkadotKeyring::EncodePrivateKeyForExport(
@@ -75,6 +75,10 @@ class PolkadotKeyring {
std::array<uint8_t, kSr25519Pkcs8Size> GetPkcs8KeyForTesting(
uint32_t account_index);
// Set the RNG used by the underlying Schnorr signing routines to be
// deterministic for the sake of testing.
void SetSignatureRngForTesting();
private:
HDKeySr25519& EnsureKeyPair(uint32_t account_index);
@@ -0,0 +1,274 @@
/* Copyright (c) 2026 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/polkadot/polkadot_signed_transfer_task.h"
#include "brave/components/brave_wallet/browser/keyring_service.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_substrate_rpc.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_wallet_service.h"
namespace brave_wallet {
PolkadotSignedTransferTask::PolkadotSignedTransferTask(
PolkadotWalletService& polkadot_wallet_service,
KeyringService& keyring_service,
mojom::AccountIdPtr sender_account_id,
std::string chain_id,
uint128_t send_amount,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> sender,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> recipient)
: polkadot_wallet_service_(polkadot_wallet_service),
keyring_service_(keyring_service),
sender_account_id_(std::move(sender_account_id)),
chain_id_(std::move(chain_id)),
send_amount_{send_amount} {
base::span(sender_).copy_from_nonoverlapping(sender);
base::span(recipient_).copy_from_nonoverlapping(recipient);
}
PolkadotSignedTransferTask::~PolkadotSignedTransferTask() = default;
bool PolkadotSignedTransferTask::IsReadyToSign() const {
return !account_info_.is_null() && signing_header_ && genesis_hash_ &&
signing_block_hash_ && runtime_version_ && chain_metadata_;
}
void PolkadotSignedTransferTask::StopWithError(std::string error_string) {
weak_ptr_factory_.InvalidateWeakPtrs();
std::move(callback_).Run(base::unexpected(std::move(error_string)));
}
PolkadotSubstrateRpc* PolkadotSignedTransferTask::GetPolkadotRpc() {
return polkadot_wallet_service_->GetPolkadotRpc();
}
void PolkadotSignedTransferTask::Start(
GenerateSignedTransferExtrinsicCallback callback) {
callback_ = std::move(callback);
GetNonce();
GetSigningHeader();
GetGenesisHash();
polkadot_wallet_service_->GetChainMetadata(
chain_id_,
base::BindOnce(&PolkadotSignedTransferTask::OnGetMetadataForSigning,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::GetNonce() {
GetPolkadotRpc()->GetAccountBalance(
chain_id_, sender_,
base::BindOnce(&PolkadotSignedTransferTask::OnGetAccountNonce,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::GetSigningHeader() {
GetPolkadotRpc()->GetBlockHeader(
chain_id_, std::nullopt,
base::BindOnce(&PolkadotSignedTransferTask::OnGetChainHeader,
weak_ptr_factory_.GetWeakPtr()));
GetPolkadotRpc()->GetFinalizedHead(
chain_id_, base::BindOnce(&PolkadotSignedTransferTask::OnGetFinalizedHead,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::GetGenesisHash() {
GetPolkadotRpc()->GetBlockHash(
chain_id_, 0,
base::BindOnce(&PolkadotSignedTransferTask::OnGetGenesisHash,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::OnGetMetadataForSigning(
base::expected<PolkadotChainMetadata, std::string> chain_metadata) {
if (!chain_metadata.has_value()) {
return StopWithError(chain_metadata.error());
}
chain_metadata_ = std::move(chain_metadata.value());
MaybeFinalizeSignTransaction();
}
void PolkadotSignedTransferTask::OnGetAccountNonce(
mojom::PolkadotAccountInfoPtr account_info,
const std::optional<std::string>& error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(account_info);
account_info_ = std::move(account_info);
MaybeFinalizeSignTransaction();
}
void PolkadotSignedTransferTask::OnGetChainHeader(
std::optional<PolkadotBlockHeader> header,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
// Current behavior of the RPC layer seems to be returning an error when
// there's no parent hash, so we always fetch it in our case vs polkadot-js
// which seems to assume very young chains where the parent hash doesn't
// necessarily exist.
CHECK(header);
GetPolkadotRpc()->GetBlockHeader(
chain_id_, header->parent_hash,
base::BindOnce(&PolkadotSignedTransferTask::OnGetParentHeader,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::OnGetParentHeader(
std::optional<PolkadotBlockHeader> header,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(header);
chain_header_ = header;
UpdateSigningHeader();
}
void PolkadotSignedTransferTask::OnGetFinalizedHead(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>> finalized_hash,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(finalized_hash);
GetPolkadotRpc()->GetBlockHeader(
chain_id_, *finalized_hash,
base::BindOnce(&PolkadotSignedTransferTask::OnGetFinalizedBlockHeader,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::OnGetFinalizedBlockHeader(
std::optional<PolkadotBlockHeader> header,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(header);
finalized_header_ = header;
UpdateSigningHeader();
}
void PolkadotSignedTransferTask::UpdateSigningHeader() {
if (!chain_header_ || !finalized_header_) {
return;
}
if (signing_header_) {
return;
}
// The polkadot-js api library chooses which block to use for the signature by
// comparing the difference in block numbers from the latest block in the
// chain and the finalized head. If the current head's block number is
// sufficiently advanced from the finalized head, we use that block to mark
// the start of the mortality period. Otherwise, we start the mortality period
// using the finalized head.
// "determine the hash to use, current when lag > max, else finalized"
// https://github.com/polkadot-js/api/blob/eb34741c871ca8d029a9706ae989ba8ce865db0f/packages/api-derive/src/tx/signingInfo.ts#L41-L71
base::CheckedNumeric<uint32_t> current = chain_header_->block_number;
base::CheckedNumeric<uint32_t> finalized = finalized_header_->block_number;
// Copy what polkadot-js does:
// https://github.com/polkadot-js/api/blob/eb34741c871ca8d029a9706ae989ba8ce865db0f/packages/api-derive/src/tx/constants.ts#L11
constexpr uint32_t kMaxFinalityLag = 5;
auto lag = current - finalized;
if (lag.IsValidAnd([](uint32_t lag) { return lag > kMaxFinalityLag; })) {
signing_header_ = chain_header_;
} else {
signing_header_ = finalized_header_;
}
GetPolkadotRpc()->GetBlockHash(
chain_id_, signing_header_->block_number,
base::BindOnce(&PolkadotSignedTransferTask::OnGetSigningBlockHash,
weak_ptr_factory_.GetWeakPtr()));
GetPolkadotRpc()->GetRuntimeVersion(
chain_id_, signing_header_->parent_hash,
base::BindOnce(&PolkadotSignedTransferTask::OnGetRuntimeVersion,
weak_ptr_factory_.GetWeakPtr()));
}
void PolkadotSignedTransferTask::OnGetGenesisHash(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>> genesis_hash,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(genesis_hash);
genesis_hash_ = genesis_hash;
MaybeFinalizeSignTransaction();
}
void PolkadotSignedTransferTask::OnGetSigningBlockHash(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>> block_hash,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(block_hash);
signing_block_hash_ = block_hash;
MaybeFinalizeSignTransaction();
}
void PolkadotSignedTransferTask::OnGetRuntimeVersion(
std::optional<PolkadotRuntimeVersion> runtime_version,
std::optional<std::string> error_string) {
if (error_string) {
return StopWithError(*error_string);
}
CHECK(runtime_version);
runtime_version_ = runtime_version;
MaybeFinalizeSignTransaction();
}
void PolkadotSignedTransferTask::MaybeFinalizeSignTransaction() {
if (!IsReadyToSign()) {
return;
}
std::array<uint8_t, 16> send_amount_bytes = {};
base::span(send_amount_bytes)
.copy_from(base::byte_span_from_ref(send_amount_));
auto signature_payload = generate_extrinsic_signature_payload(
*chain_metadata_.value(), account_info_->nonce, send_amount_bytes,
recipient_, runtime_version_->spec_version,
runtime_version_->transaction_version, signing_header_->block_number,
*genesis_hash_, *signing_block_hash_);
auto signature = keyring_service_->SignMessageByPolkadotKeyring(
sender_account_id_, signature_payload);
CHECK(signature);
auto sender_pubkey = keyring_service_->GetPolkadotPubKey(sender_account_id_);
CHECK(sender_pubkey);
auto signed_extrinsic = make_signed_extrinsic(
*chain_metadata_.value(), *sender_pubkey, recipient_, send_amount_bytes,
*signature, signing_header_->block_number, account_info_->nonce);
std::move(callback_).Run(base::ok(
std::vector<uint8_t>(signed_extrinsic.begin(), signed_extrinsic.end())));
}
} // namespace brave_wallet
@@ -0,0 +1,199 @@
/* Copyright (c) 2026 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_POLKADOT_POLKADOT_SIGNED_TRANSFER_TASK_H_
#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_POLKADOT_POLKADOT_SIGNED_TRANSFER_TASK_H_
#include "brave/components/brave_wallet/browser/polkadot/polkadot_extrinsic.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_substrate_rpc.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
namespace brave_wallet {
class PolkadotWalletService;
class KeyringService;
// clang-format off
/*
We need several different RPC calls to create a reliable signing payload as
described by the spec.
* We need a usable sender nonce to calculate the mortality of the extrinsic.
* We need the genesis hash of the chain.
* We need the block hash and number of the block in the chain we're using to
start the mortality period.
* We need the runtime info which provides the spec version and transaction
version.
We also chain dependent tasks. For example, we fetch the parent block of the
latest current block when doing signing block selection.
The nature of the RPC calls looks roughly akin to this:
Start
GetNonce GetSigningHeader GetGenesisHash GetChainMetadata
OnGetAccount OnGetChainHeader OnGetFinalizedHead OnGetGenesisHash OnGetMetadataForSigning
Nonce
OnGetParentHeader OnGetFinalizedBlockHeader
UpdateSigningHeader
OnGetSigningBlockHash OnGetRuntimeVersion
MaybeFinalizeSignTransaction
// clang-format on
*/
struct PolkadotSignedTransferTask {
public:
using GenerateSignedTransferExtrinsicCallback =
base::OnceCallback<void(base::expected<std::vector<uint8_t>, std::string>)>;
PolkadotSignedTransferTask(
PolkadotWalletService& polkadot_wallet_service,
KeyringService& keyring_service,
mojom::AccountIdPtr sender_account_id,
std::string chain_id,
uint128_t send_amount,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> sender,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> recipient);
~PolkadotSignedTransferTask();
void Start(GenerateSignedTransferExtrinsicCallback callback);
private:
void StopWithError(std::string error_string);
bool IsReadyToSign() const;
PolkadotSubstrateRpc* GetPolkadotRpc();
// Signed extrinsics require a nonce from the sender, which we can pull by
// querying the account information.
// https://spec.polkadot.network/id-extrinsics#defn-extra-data
void GetNonce();
// Signed extrinsics require the hash of the block chain at genesis which we
// can trivially fetch using RPC calls.
// https://spec.polkadot.network/id-extrinsics#defn-extrinsic-signature
void GetGenesisHash();
// Extrinsic signing requires choosing a block to start the mortality
// period, whose header we must obtain. Recreate the routines used by
// polkadot-js where the canonical finalized head is compared to the latest
// head:
// https://github.com/polkadot-js/api/blob/eb34741c871ca8d029a9706ae989ba8ce865db0f/packages/api-derive/src/tx/signingInfo.ts#L41-L71
void GetSigningHeader();
// Internal helper called by the independent network fetches that retrieve
// the latest finalized head and the latest head in the block chain.
void UpdateSigningHeader();
// Associates the chain metadata (pallet indices, call indices) with the
// current in-flight transaction.
void OnGetMetadataForSigning(
base::expected<PolkadotChainMetadata, std::string>);
// Associate the account information with the current in-flight transaction.
// Note that only the nonce is currently used but it may prove useful later
// to have access to the kinds of funds the sender has.
void OnGetAccountNonce(mojom::PolkadotAccountInfoPtr,
const std::optional<std::string>&);
// Fetch the header of the latest block in the chain.
// https://github.com/w3f/PSPs/blob/b6d570173146e7a012cf43d270177e02ed886e2e/PSPs/drafts/psp-6.md#192-chain_getheader
void OnGetChainHeader(std::optional<PolkadotBlockHeader>,
std::optional<std::string>);
// Internal helper used a follow-up for getting the chain's latest header.
// This parent header's block hash may need to be fetched later.
void OnGetParentHeader(std::optional<PolkadotBlockHeader>,
std::optional<std::string>);
// Internal helper used to take the block hash of the finalized head and
// then fetch the block header itself.
void OnGetFinalizedHead(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>>,
std::optional<std::string>);
// Internal helper used to associated the finalized header with the current
// in-flight transaction.
void OnGetFinalizedBlockHeader(std::optional<PolkadotBlockHeader>,
std::optional<std::string>);
// Internal helper used to associate the chain's genesis hash with the
// current in-flight transaction.
void OnGetGenesisHash(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>>,
std::optional<std::string>);
// Internal helper used to associate the block hash of the block that marks
// the start of the mortality period (either the latest head or finalized).
void OnGetSigningBlockHash(
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>>,
std::optional<std::string>);
// Internal helper that associates the spec version and transaction version
// with the in-flight transaction.
void OnGetRuntimeVersion(std::optional<PolkadotRuntimeVersion>,
std::optional<std::string>);
// The finalization process that actually generates the signature payload,
// signs it using the sender's private key and then invoking the user's
// callback with the result of the operation.
void MaybeFinalizeSignTransaction();
base::raw_ref<PolkadotWalletService> polkadot_wallet_service_;
base::raw_ref<KeyringService> keyring_service_;
mojom::AccountIdPtr sender_account_id_;
std::string chain_id_;
uint128_t send_amount_ = 0;
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> sender_ = {};
std::array<uint8_t, kPolkadotSubstrateAccountIdSize> recipient_ = {};
GenerateSignedTransferExtrinsicCallback callback_;
std::optional<PolkadotChainMetadata> chain_metadata_;
mojom::PolkadotAccountInfoPtr account_info_;
std::optional<PolkadotBlockHeader> chain_header_;
std::optional<PolkadotBlockHeader> finalized_header_;
std::optional<PolkadotBlockHeader> signing_header_;
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>> genesis_hash_;
std::optional<std::array<uint8_t, kPolkadotBlockHashSize>>
signing_block_hash_;
std::optional<PolkadotRuntimeVersion> runtime_version_;
base::WeakPtrFactory<PolkadotSignedTransferTask> weak_ptr_factory_{this};
};
} // namespace brave_wallet
#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_POLKADOT_POLKADOT_SIGNED_TRANSFER_TASK_H_
@@ -42,6 +42,7 @@ namespace polkadot_substrate_rpc_responses {
// chain_getHeader's RPC structure is described here:
// https://github.com/w3f/PSPs/blob/b6d570173146e7a012cf43d270177e02ed886e2e/PSPs/drafts/psp-6.md#192-chain_getheader
// TODO(https://github.com/brave/brave-browser/issues/52318): this method needs to be updated to use `DOMString? parentHash;`
dictionary ChainHeader {
// The hex-encoded hash of the parent block's header.
@@ -83,7 +83,7 @@ void PolkadotTxManager::AddUnapprovedPolkadotTransaction(
void PolkadotTxManager::OnGetChainMetadataForUnapproved(
mojom::NewPolkadotTransactionParamsPtr params,
AddUnapprovedPolkadotTransactionCallback callback,
const base::expected<PolkadotChainMetadata, std::string>& chain_metadata) {
base::expected<PolkadotChainMetadata, std::string> chain_metadata) {
if (!chain_metadata.has_value()) {
return std::move(callback).Run(false, "", chain_metadata.error());
}
@@ -79,7 +79,7 @@ class PolkadotTxManager : public TxManager,
void OnGetChainMetadataForUnapproved(
mojom::NewPolkadotTransactionParamsPtr params,
AddUnapprovedPolkadotTransactionCallback callback,
const base::expected<PolkadotChainMetadata, std::string>& chain_metadata);
base::expected<PolkadotChainMetadata, std::string> chain_metadata);
// PolkadotBlockTracker::Observer
void OnLatestBlock(const std::string& chain_id, uint64_t block_num) override;
@@ -79,6 +79,10 @@ void PolkadotWalletService::Reset() {
weak_ptr_factory_.InvalidateWeakPtrs();
}
PolkadotSubstrateRpc* PolkadotWalletService::GetPolkadotRpc() {
return &polkadot_substrate_rpc_;
}
void PolkadotWalletService::GetNetworkName(mojom::AccountIdPtr account_id,
GetNetworkNameCallback callback) {
std::string chain_id = GetNetworkForPolkadotAccount(account_id);
@@ -142,4 +146,39 @@ void PolkadotWalletService::OnInitializeChainMetadata(
}
}
void PolkadotWalletService::GenerateSignedTransferExtrinsic(
std::string chain_id,
mojom::AccountIdPtr account_id,
uint128_t send_amount,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> recipient,
GenerateSignedTransferExtrinsicCallback callback) {
auto pubkey = keyring_service_->GetPolkadotPubKey(account_id);
if (!pubkey) {
return std::move(callback).Run(
base::unexpected(WalletInternalErrorMessage()));
}
auto transaction_state = std::make_unique<PolkadotSignedTransferTask>(
*this, *keyring_service_, std::move(account_id), std::move(chain_id),
send_amount, *pubkey, recipient);
auto& transaction = *transaction_state;
auto [pos, inserted] =
polkadot_sign_transactions_.insert(std::move(transaction_state));
CHECK(inserted);
transaction.Start(base::BindOnce(
&PolkadotWalletService::OnGenerateSignedTransferExtrinsic,
weak_ptr_factory_.GetWeakPtr(), pos->get(), std::move(callback)));
}
void PolkadotWalletService::OnGenerateSignedTransferExtrinsic(
PolkadotSignedTransferTask* transaction_state,
GenerateSignedTransferExtrinsicCallback callback,
base::expected<std::vector<uint8_t>, std::string> signed_extrinsic) {
polkadot_sign_transactions_.erase(transaction_state);
std::move(callback).Run(std::move(signed_extrinsic));
}
} // namespace brave_wallet
@@ -8,10 +8,12 @@
#include "brave/components/brave_wallet/browser/keyring_service_observer_base.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_extrinsic.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_signed_transfer_task.h"
#include "brave/components/brave_wallet/browser/polkadot/polkadot_substrate_rpc.h"
#include "brave/components/brave_wallet/common/brave_wallet.mojom.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
namespace brave_wallet {
@@ -23,7 +25,10 @@ class PolkadotWalletService : public mojom::PolkadotWalletService,
public KeyringServiceObserverBase {
public:
using GetChainMetadataCallback = base::OnceCallback<void(
const base::expected<PolkadotChainMetadata, std::string>&)>;
base::expected<PolkadotChainMetadata, std::string>)>;
using GenerateSignedTransferExtrinsicCallback = base::OnceCallback<void(
base::expected<std::vector<uint8_t>, std::string>)>;
PolkadotWalletService(
KeyringService& keyring_service,
@@ -38,6 +43,8 @@ class PolkadotWalletService : public mojom::PolkadotWalletService,
// Invalidates all the weak ptrs in use by this service.
void Reset();
PolkadotSubstrateRpc* GetPolkadotRpc();
// Get the name of the chain currently pointed to by the current network
// configuration.
void GetNetworkName(mojom::AccountIdPtr account_id,
@@ -53,6 +60,18 @@ class PolkadotWalletService : public mojom::PolkadotWalletService,
void GetChainMetadata(std::string_view chain_id,
GetChainMetadataCallback callback);
// Generates an encoded byte array representing a transfer_allow_death call,
// suitable for sending over the network as hex, signed using the account's
// private key. The signed extrinsic can be submitted directly using
// author_submitExtrinsic or payment information can be queried using
// payment_queryInfo.
void GenerateSignedTransferExtrinsic(
std::string chain_id,
mojom::AccountIdPtr account_id,
uint128_t send_amount,
base::span<const uint8_t, kPolkadotSubstrateAccountIdSize> recipient,
GenerateSignedTransferExtrinsicCallback callback);
private:
// KeyringServiceObserverBase:
void Unlocked() override;
@@ -70,6 +89,11 @@ class PolkadotWalletService : public mojom::PolkadotWalletService,
const std::optional<std::string>&,
const std::optional<std::string>&);
void OnGenerateSignedTransferExtrinsic(
PolkadotSignedTransferTask* transaction_state,
GenerateSignedTransferExtrinsicCallback callback,
base::expected<std::vector<uint8_t>, std::string> signed_extrinsic);
const raw_ref<KeyringService> keyring_service_;
mojo::ReceiverSet<mojom::PolkadotWalletService> receivers_;
@@ -82,6 +106,9 @@ class PolkadotWalletService : public mojom::PolkadotWalletService,
std::vector<GetChainMetadataCallback> testnet_chain_metadata_callbacks_;
PolkadotSubstrateRpc polkadot_substrate_rpc_;
absl::flat_hash_set<std::unique_ptr<PolkadotSignedTransferTask>>
polkadot_sign_transactions_;
mojo::Receiver<brave_wallet::mojom::KeyringServiceObserver>
keyring_service_observer_receiver_{this};
File diff suppressed because it is too large Load Diff
@@ -17,6 +17,13 @@ inline constexpr size_t kSolanaHashSize = 32;
// fragment header)
inline constexpr size_t kSolanaMaxTxSize = 1232;
// https://docs.rs/schnorrkel/0.11.4/schnorrkel/keys/index.html#constants
inline constexpr size_t kSr25519SeedSize = 32;
inline constexpr size_t kSr25519SecretKeySize = 64;
inline constexpr size_t kSr25519PublicKeySize = 32;
inline constexpr size_t kSr25519SignatureSize = 64;
inline constexpr size_t kSr25519Pkcs8Size = 117;
} // namespace brave_wallet
#endif // BRAVE_COMPONENTS_BRAVE_WALLET_COMMON_BRAVE_WALLET_CONSTANTS_H_