Skip to content

Commit

Permalink
[WIP]: Truncated address formats
Browse files Browse the repository at this point in the history
  • Loading branch information
hdevalence committed Dec 3, 2024
1 parent dcc8386 commit 4344281
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
19 changes: 18 additions & 1 deletion crates/core/keys/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub use view::AddressView;

use crate::{fmd, ka, keys::Diversifier};

pub const TRUNCATED_ADDRESS_BECH32_PREFIX: &str = "ptrunc";

/// The length of an [`Address`] in bytes.
pub const ADDRESS_LEN_BYTES: usize = 80;

Expand Down Expand Up @@ -319,7 +321,22 @@ impl std::str::FromStr for Address {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.starts_with(bech32str::compat_address::BECH32_PREFIX) {
if s.starts_with(TRUNCATED_ADDRESS_BECH32_PREFIX) {
let dzero = Diversifier([0u8; 16]);

let pk_dzero_bytes: [u8; 32] =
bech32str::decode(s, TRUNCATED_ADDRESS_BECH32_PREFIX, bech32str::Bech32)?
.try_into()
.map_err(|bytes: Vec<u8>| {
anyhow::anyhow!("wrong length {}, expected 32", bytes.len())
})?;
let pk_dzero = ka::Public(pk_dzero_bytes);

let ck_id = fmd::ClueKey([0u8; 32]);

Self::from_components(dzero, pk_dzero, ck_id)
.ok_or_else(|| anyhow::anyhow!("could not reconstruct truncated address"))
} else if s.starts_with(bech32str::compat_address::BECH32_PREFIX) {
pb::Address {
inner: bech32str::decode(
s,
Expand Down
52 changes: 52 additions & 0 deletions crates/core/keys/src/keys/ivk.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ark_ff::{PrimeField, Zero};
use penumbra_proto::serializers::bech32str;
use rand_core::{CryptoRng, RngCore};

use ark_r1cs_std::prelude::*;
Expand Down Expand Up @@ -44,6 +45,25 @@ impl IncomingViewingKey {
)
}

/// Derive the (encoding of the) truncated address for the given IVK.
///
/// This intentionally returns a `String` rather than an `Address`, as it's not
/// safe to truncate arbitrary addresses.
pub fn truncated_address(&self) -> String {
// The truncated address uses an all-zero diversifier.
let dzero = Diversifier([0u8; 16]);
let g_dzero = dzero.diversified_generator();
let pk_dzero = self.ivk.diversified_public(&g_dzero);

let encoding = bech32str::encode(
&pk_dzero.0,
crate::address::TRUNCATED_ADDRESS_BECH32_PREFIX,
bech32str::Bech32,
);

encoding
}

/// Derive an ephemeral address for the provided account.
pub fn ephemeral_address<R: RngCore + CryptoRng>(
&self,
Expand Down Expand Up @@ -185,6 +205,38 @@ mod test {

use super::*;

#[test]
fn truncated_address_generation_and_parsing() {
let rng = rand::rngs::OsRng;
let spend_key =
SpendKey::from_seed_phrase_bip44(SeedPhrase::generate(rng), &Bip44Path::new(0));
let ivk = spend_key.full_viewing_key().incoming();

let truncated_address_str = ivk.truncated_address();

let reconstructed: Address = truncated_address_str
.parse()
.expect("can parse truncated address");

assert!(ivk.views_address(&reconstructed));

let address_index = ivk.address_index(&reconstructed).expect("views address");

let actual_address = ivk.payment_address(address_index).0;

// The diversifiers should match
assert_eq!(reconstructed.diversifier(), actual_address.diversifier());
// The transmission keys should match
assert_eq!(reconstructed.transmission_key(), actual_address.transmission_key());
// The clue keys should not match, as the clue key is zeroed out
assert_ne!(reconstructed.clue_key(), actual_address.clue_key());

println!("Truncated address: {}", truncated_address_str);
println!("Reconstructed address: {}", reconstructed);
println!("Address index: {:?}", address_index);
println!("Actual address for index: {}", actual_address);
}

#[test]
fn views_address_succeeds_on_own_address() {
let rng = rand::rngs::OsRng;
Expand Down

0 comments on commit 4344281

Please sign in to comment.