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

Add option to find public key from an address #1503

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 3 additions & 2 deletions test/functional/test_framework/wallet_cli_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ async def set_lookahead_size(self, size: int, force_reduce: bool) -> str:
return await self._write_command(f"wallet-set-lookahead-size {size} {i_know_what_i_am_doing}\n")

async def new_public_key(self) -> bytes:
public_key = await self._write_command("address-new-public-key\n")
addr = await self.new_address()
public_key = await self._write_command(f"address-reveal-public-key {addr}\n")

# remove the pub key enum value, the first one byte
pub_key_bytes = bytes.fromhex(public_key)[1:]
pub_key_bytes = bytes.fromhex(public_key.split('\n')[1])[1:]
return pub_key_bytes

async def new_address(self) -> str:
Expand Down
3 changes: 2 additions & 1 deletion test/functional/test_framework/wallet_rpc_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ async def select_account(self, account_index: int) -> str:
return "Success"

async def new_public_key(self) -> bytes:
public_key = self._write_command("address_new_public_key", [self.account])['result']['public_key']
addr = await self.new_address()
public_key = self._write_command("address_reveal_public_key", [self.account, addr])['result']['public_key']

# remove the pub key enum value, the first one byte
pub_key_bytes = bytes.fromhex(public_key)[1:]
Expand Down
13 changes: 7 additions & 6 deletions wallet/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,13 +1188,14 @@ impl Account {
Ok(self.key_chain.issue_address(db_tx, purpose)?)
}

/// Get a new public key that hasn't been used before
pub fn get_new_public_key<B: storage::Backend>(
&mut self,
db_tx: &mut StoreTxRw<B>,
purpose: KeyPurpose,
/// Get the corresponding public key for a given public key hash
pub fn find_corresponding_pub_key(
&self,
public_key_hash: &PublicKeyHash,
) -> WalletResult<PublicKey> {
Ok(self.key_chain.issue_key(db_tx, purpose)?.into_public_key())
self.key_chain
.get_public_key_from_public_key_hash(public_key_hash)
.ok_or(WalletError::AddressNotFound)
}

pub fn get_all_issued_addresses(&self) -> BTreeMap<ChildNumber, Address<Destination>> {
Expand Down
11 changes: 11 additions & 0 deletions wallet/src/key_chain/account_key_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,17 @@ impl AccountKeyChain {
.any(|purpose| self.get_leaf_key_chain(*purpose).is_public_key_hash_mine(pubkey_hash))
}

/// Find the corresponding public key for a given public key hash
pub fn get_public_key_from_public_key_hash(
&self,
pubkey_hash: &PublicKeyHash,
) -> Option<PublicKey> {
KeyPurpose::ALL.iter().find_map(|purpose| {
self.get_leaf_key_chain(*purpose)
.get_public_key_from_public_key_hash(pubkey_hash)
})
}

/// Derive addresses until there are lookahead unused ones
pub fn top_up_all(&mut self, db_tx: &mut impl WalletStorageWriteLocked) -> KeyChainResult<()> {
let lookahead_size = self.lookahead_size();
Expand Down
8 changes: 8 additions & 0 deletions wallet/src/key_chain/leaf_key_chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,14 @@ impl LeafKeySoftChain {
self.public_key_hash_to_index.get(pkh).copied()
}

/// Get public key for public key hash or None if no key found
pub fn get_public_key_from_public_key_hash(&self, pkh: &PublicKeyHash) -> Option<PublicKey> {
let child_number = self.public_key_hash_to_index.get(pkh)?;
self.derived_public_keys
.get(child_number)
.map(|pk| pk.clone().into_public_key())
}

/// Mark a specific key as used in the key pool. This will update the last used key index if
/// necessary. Returns false if a key was found and set to used.
fn mark_child_key_as_used(
Expand Down
19 changes: 15 additions & 4 deletions wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ pub enum WalletError {
InputCannotBeSigned,
#[error("Failed to convert partially signed tx to signed")]
FailedToConvertPartiallySignedTx(PartiallySignedTransaction),
#[error("The specified address is not found in this wallet")]
AddressNotFound,
}

/// Result type used for the wallet
Expand Down Expand Up @@ -924,10 +926,19 @@ impl<B: storage::Backend> Wallet<B> {
})
}

pub fn get_new_public_key(&mut self, account_index: U31) -> WalletResult<PublicKey> {
self.for_account_rw(account_index, |account, db_tx| {
account.get_new_public_key(db_tx, KeyPurpose::ReceiveFunds)
})
pub fn find_public_key(
&mut self,
account_index: U31,
address: Destination,
) -> WalletResult<PublicKey> {
let account = self.get_account(account_index)?;
match address {
Destination::Address(addr) => account.find_corresponding_pub_key(&addr),
Destination::PublicKey(pk) => Ok(pk),
Destination::ScriptHash(_)
| Destination::AnyoneCanSpend
| Destination::ClassicMultisig(_) => Err(WalletError::NoUtxos),
}
}

pub fn get_transaction_list(
Expand Down
30 changes: 15 additions & 15 deletions wallet/src/wallet/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,13 +806,13 @@ fn wallet_accounts_creation() {
let error = wallet.create_next_account(None).err().unwrap();
assert_eq!(error, WalletError::EmptyLastAccount);

let acc1_pk = wallet.get_new_public_key(res.0).unwrap();
let acc1_pk = wallet.get_new_address(res.0).unwrap().1;
let tx = wallet
.create_transaction_to_addresses(
DEFAULT_ACCOUNT_INDEX,
[TxOutput::Transfer(
OutputValue::Coin(Amount::from_atoms(1)),
Destination::PublicKey(acc1_pk),
acc1_pk.decode_object(&chain_config).unwrap(),
)],
vec![],
FeeRate::from_amount_per_kb(Amount::ZERO),
Expand Down Expand Up @@ -1303,7 +1303,7 @@ fn create_stake_pool_and_list_pool_ids(#[case] seed: Seed) {

let pool_amount = block1_amount;

let decommission_key = wallet.get_new_public_key(DEFAULT_ACCOUNT_INDEX).unwrap();
let decommission_key = wallet.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1;

let stake_pool_transaction = wallet
.create_stake_pool_tx(
Expand All @@ -1314,7 +1314,7 @@ fn create_stake_pool_and_list_pool_ids(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key.clone()),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down Expand Up @@ -1346,7 +1346,7 @@ fn create_stake_pool_and_list_pool_ids(#[case] seed: Seed) {
let (pool_id, pool_data) = pool_ids.first().unwrap();
assert_eq!(
pool_data.decommission_key,
Destination::PublicKey(decommission_key)
decommission_key.decode_object(&chain_config).unwrap()
);
assert_eq!(
&pool_data.utxo_outpoint,
Expand Down Expand Up @@ -3666,7 +3666,7 @@ fn decommission_pool_wrong_account(#[case] seed: Seed) {
let res = wallet.create_next_account(Some("name".into())).unwrap();
assert_eq!(res, (U31::from_u32(1).unwrap(), Some("name".into())));

let decommission_key = wallet.get_new_public_key(acc_1_index).unwrap();
let decommission_key = wallet.get_new_address(acc_1_index).unwrap().1;

let stake_pool_transaction = wallet
.create_stake_pool_tx(
Expand All @@ -3677,7 +3677,7 @@ fn decommission_pool_wrong_account(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down Expand Up @@ -3767,7 +3767,7 @@ fn decommission_pool_request_wrong_account(#[case] seed: Seed) {
let res = wallet.create_next_account(Some("name".into())).unwrap();
assert_eq!(res, (U31::from_u32(1).unwrap(), Some("name".into())));

let decommission_key = wallet.get_new_public_key(acc_1_index).unwrap();
let decommission_key = wallet.get_new_address(acc_1_index).unwrap().1;

let stake_pool_transaction = wallet
.create_stake_pool_tx(
Expand All @@ -3778,7 +3778,7 @@ fn decommission_pool_request_wrong_account(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down Expand Up @@ -3851,7 +3851,7 @@ fn sign_decommission_pool_request_between_accounts(#[case] seed: Seed) {
let res = wallet.create_next_account(Some("name".into())).unwrap();
assert_eq!(res, (U31::from_u32(1).unwrap(), Some("name".into())));

let decommission_key = wallet.get_new_public_key(acc_1_index).unwrap();
let decommission_key = wallet.get_new_address(acc_1_index).unwrap().1;

let stake_pool_transaction = wallet
.create_stake_pool_tx(
Expand All @@ -3862,7 +3862,7 @@ fn sign_decommission_pool_request_between_accounts(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down Expand Up @@ -3930,7 +3930,7 @@ fn sign_decommission_pool_request_cold_wallet(#[case] seed: Seed) {
let another_mnemonic =
"legal winner thank year wave sausage worth useful legal winner thank yellow";
let mut cold_wallet = create_wallet_with_mnemonic(chain_config.clone(), another_mnemonic);
let decommission_key = cold_wallet.get_new_public_key(DEFAULT_ACCOUNT_INDEX).unwrap();
let decommission_key = cold_wallet.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1;

let coin_balance = get_coin_balance(&hot_wallet);
assert_eq!(coin_balance, Amount::ZERO);
Expand Down Expand Up @@ -3959,7 +3959,7 @@ fn sign_decommission_pool_request_cold_wallet(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down Expand Up @@ -4026,7 +4026,7 @@ fn filter_pools(#[case] seed: Seed) {
let another_mnemonic =
"legal winner thank year wave sausage worth useful legal winner thank yellow";
let mut wallet2 = create_wallet_with_mnemonic(chain_config.clone(), another_mnemonic);
let decommission_key = wallet2.get_new_public_key(DEFAULT_ACCOUNT_INDEX).unwrap();
let decommission_key = wallet2.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1;

let coin_balance = get_coin_balance(&wallet1);
assert_eq!(coin_balance, Amount::ZERO);
Expand All @@ -4053,7 +4053,7 @@ fn filter_pools(#[case] seed: Seed) {
amount: pool_amount,
margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng),
cost_per_block: Amount::ZERO,
decommission_key: Destination::PublicKey(decommission_key),
decommission_key: decommission_key.decode_object(&chain_config).unwrap(),
},
)
.unwrap();
Expand Down
13 changes: 8 additions & 5 deletions wallet/wallet-cli-lib/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ pub enum ColdWalletCommand {
NewAddress,

/// Generate a new unused public key
#[clap(name = "address-new-public-key")]
NewPublicKey,
#[clap(name = "address-reveal-public-key")]
RevealPublicKey { public_key_hash: String },

/// Show receive-addresses with their usage state.
/// Note that whether an address is used isn't based on the wallet,
Expand Down Expand Up @@ -870,11 +870,14 @@ where
Ok(ConsoleCommand::Print(address.address))
}

ColdWalletCommand::NewPublicKey => {
ColdWalletCommand::RevealPublicKey { public_key_hash } => {
let selected_account = self.get_selected_acc()?;
let public_key =
self.wallet_rpc.issue_public_key(selected_account).await?.public_key;
Ok(ConsoleCommand::Print(public_key))
self.wallet_rpc.find_public_key(selected_account, public_key_hash).await?;
Ok(ConsoleCommand::Print(format!(
"Public key as hex and as address:\n{}\n{}",
public_key.public_key_hex, public_key.public_key_address
)))
}

ColdWalletCommand::ShowReceiveAddresses => {
Expand Down
7 changes: 5 additions & 2 deletions wallet/wallet-controller/src/synced_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,12 @@ impl<'a, T: NodeInterface, W: WalletEvents> SyncedController<'a, T, W> {
.map_err(ControllerError::WalletError)
}

pub fn new_public_key(&mut self) -> Result<PublicKey, ControllerError<T>> {
pub fn find_public_key(
&mut self,
address: Destination,
) -> Result<PublicKey, ControllerError<T>> {
self.wallet
.get_new_public_key(self.account_index)
.find_public_key(self.account_index, address)
.map_err(ControllerError::WalletError)
}

Expand Down
3 changes: 2 additions & 1 deletion wallet/wallet-rpc-lib/src/rpc/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ trait WalletRpc {
#[method(name = "address_new")]
async fn issue_address(&self, account_index: AccountIndexArg) -> rpc::RpcResult<AddressInfo>;

#[method(name = "address_new_public_key")]
#[method(name = "address_reveal_public_key")]
async fn issue_public_key(
&self,
account_index: AccountIndexArg,
address: String,
) -> rpc::RpcResult<PublicKeyInfo>;

#[method(name = "account_balance")]
Expand Down
15 changes: 12 additions & 3 deletions wallet/wallet-rpc-lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use self::types::{
VrfPublicKeyInfo,
};

#[derive(Clone)]
pub struct WalletRpc<N: Clone> {
wallet: WalletHandle<N>,
node: N,
Expand Down Expand Up @@ -195,17 +196,25 @@ impl<N: NodeInterface + Clone + Send + Sync + 'static> WalletRpc<N> {
Ok(AddressInfo::new(child_number, destination))
}

pub async fn issue_public_key(&self, account_index: U31) -> WRpcResult<PublicKeyInfo, N> {
pub async fn find_public_key(
&self,
account_index: U31,
address: String,
) -> WRpcResult<PublicKeyInfo, N> {
let config = ControllerConfig { in_top_x_mb: 5 }; // irrelevant for issuing addresses
let address = Address::from_str(&self.chain_config, &address)
.and_then(|addr| addr.decode_object(&self.chain_config))
.map_err(|_| RpcError::InvalidAddress)?;

let publick_key = self
.wallet
.call_async(move |w| {
Box::pin(async move {
w.synced_controller(account_index, config).await?.new_public_key()
w.synced_controller(account_index, config).await?.find_public_key(address)
})
})
.await??;
Ok(PublicKeyInfo::new(publick_key))
Ok(PublicKeyInfo::new(publick_key, &self.chain_config))
}

pub async fn get_legacy_vrf_public_key(
Expand Down
3 changes: 2 additions & 1 deletion wallet/wallet-rpc-lib/src/rpc/server_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ impl<N: NodeInterface + Clone + Send + Sync + 'static + Debug> WalletRpcServer f
async fn issue_public_key(
&self,
account_index: AccountIndexArg,
address: String,
) -> rpc::RpcResult<PublicKeyInfo> {
rpc::handle_result(self.issue_public_key(account_index.index::<N>()?).await)
rpc::handle_result(self.find_public_key(account_index.index::<N>()?, address).await)
}

async fn get_issued_addresses(
Expand Down
10 changes: 7 additions & 3 deletions wallet/wallet-rpc-lib/src/rpc/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,17 @@ impl AddressWithUsageInfo {

#[derive(Debug, Eq, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
pub struct PublicKeyInfo {
pub public_key: String,
pub public_key_hex: String,
pub public_key_address: String,
}

impl PublicKeyInfo {
pub fn new(pub_key: PublicKey) -> Self {
pub fn new(pub_key: PublicKey, chain_config: &ChainConfig) -> Self {
Self {
public_key: pub_key.hex_encode(),
public_key_hex: pub_key.hex_encode(),
public_key_address: Address::new(chain_config, &Destination::PublicKey(pub_key))
.expect("addressable")
.to_string(),
}
}
}
Expand Down
Loading