Merge pull request #109 from str4d/paymentaddress-encapsulation
PaymentAddress encapsulation
This commit is contained in:
commit
2b6fbfd2d1
|
@ -112,8 +112,7 @@ extern "C" {
|
||||||
bool librustzcash_sapling_output_proof(
|
bool librustzcash_sapling_output_proof(
|
||||||
void *ctx,
|
void *ctx,
|
||||||
const unsigned char *esk,
|
const unsigned char *esk,
|
||||||
const unsigned char *diversifier,
|
const unsigned char *payment_address,
|
||||||
const unsigned char *pk_d,
|
|
||||||
const unsigned char *rcm,
|
const unsigned char *rcm,
|
||||||
const uint64_t value,
|
const uint64_t value,
|
||||||
unsigned char *cv,
|
unsigned char *cv,
|
||||||
|
|
|
@ -927,8 +927,7 @@ pub extern "system" fn librustzcash_sprout_verify(
|
||||||
pub extern "system" fn librustzcash_sapling_output_proof(
|
pub extern "system" fn librustzcash_sapling_output_proof(
|
||||||
ctx: *mut SaplingProvingContext,
|
ctx: *mut SaplingProvingContext,
|
||||||
esk: *const [c_uchar; 32],
|
esk: *const [c_uchar; 32],
|
||||||
diversifier: *const [c_uchar; 11],
|
payment_address: *const [c_uchar; 43],
|
||||||
pk_d: *const [c_uchar; 32],
|
|
||||||
rcm: *const [c_uchar; 32],
|
rcm: *const [c_uchar; 32],
|
||||||
value: u64,
|
value: u64,
|
||||||
cv: *mut [c_uchar; 32],
|
cv: *mut [c_uchar; 32],
|
||||||
|
@ -940,26 +939,12 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Grab the diversifier from the caller.
|
// Grab the payment address from the caller
|
||||||
let diversifier = Diversifier(unsafe { *diversifier });
|
let payment_address =
|
||||||
|
match PaymentAddress::<Bls12>::from_bytes(unsafe { &*payment_address }, &JUBJUB) {
|
||||||
// Grab pk_d from the caller.
|
Some(pa) => pa,
|
||||||
let pk_d = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*pk_d })[..], &JUBJUB) {
|
None => return false,
|
||||||
Ok(p) => p,
|
};
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// pk_d should be prime order.
|
|
||||||
let pk_d = match pk_d.as_prime_order(&JUBJUB) {
|
|
||||||
Some(p) => p,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Construct a payment address
|
|
||||||
let payment_address = PaymentAddress {
|
|
||||||
pk_d: pk_d,
|
|
||||||
diversifier: diversifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
// The caller provides the commitment randomness for the output note
|
// The caller provides the commitment randomness for the output note
|
||||||
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
|
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
|
||||||
|
@ -1230,14 +1215,7 @@ pub extern "system" fn librustzcash_zip32_xfvk_address(
|
||||||
let addr_ret = unsafe { &mut *addr_ret };
|
let addr_ret = unsafe { &mut *addr_ret };
|
||||||
|
|
||||||
j_ret.copy_from_slice(&(addr.0).0);
|
j_ret.copy_from_slice(&(addr.0).0);
|
||||||
addr_ret
|
addr_ret.copy_from_slice(&addr.1.to_bytes());
|
||||||
.get_mut(..11)
|
|
||||||
.unwrap()
|
|
||||||
.copy_from_slice(&addr.1.diversifier.0);
|
|
||||||
addr.1
|
|
||||||
.pk_d
|
|
||||||
.write(addr_ret.get_mut(11..).unwrap())
|
|
||||||
.expect("should be able to serialize a PaymentAddress");
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ fn test_key_agreement() {
|
||||||
|
|
||||||
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
// Serialize pk_d for the call to librustzcash_sapling_ka_agree
|
||||||
let mut addr_pk_d = [0u8; 32];
|
let mut addr_pk_d = [0u8; 32];
|
||||||
addr.pk_d.write(&mut addr_pk_d[..]).unwrap();
|
addr.pk_d().write(&mut addr_pk_d[..]).unwrap();
|
||||||
|
|
||||||
assert!(librustzcash_sapling_ka_agree(
|
assert!(librustzcash_sapling_ka_agree(
|
||||||
&addr_pk_d,
|
&addr_pk_d,
|
||||||
|
@ -59,7 +59,7 @@ fn test_key_agreement() {
|
||||||
// using the diversifier and esk.
|
// using the diversifier and esk.
|
||||||
let mut epk = [0u8; 32];
|
let mut epk = [0u8; 32];
|
||||||
assert!(librustzcash_sapling_ka_derivepublic(
|
assert!(librustzcash_sapling_ka_derivepublic(
|
||||||
&addr.diversifier.0,
|
&addr.diversifier().0,
|
||||||
&esk,
|
&esk,
|
||||||
&mut epk
|
&mut epk
|
||||||
));
|
));
|
||||||
|
|
|
@ -707,7 +707,7 @@ fn key_components() {
|
||||||
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap();
|
||||||
{
|
{
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
addr.pk_d.write(&mut vec).unwrap();
|
addr.pk_d().write(&mut vec).unwrap();
|
||||||
assert_eq!(&vec, &tv.default_pk_d);
|
assert_eq!(&vec, &tv.default_pk_d);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,10 +7,7 @@ use bech32::{self, Error, FromBase32, ToBase32};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
jubjub::edwards,
|
primitives::PaymentAddress,
|
||||||
primitives::{Diversifier, PaymentAddress},
|
|
||||||
};
|
|
||||||
use zcash_primitives::{
|
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||||
JUBJUB,
|
JUBJUB,
|
||||||
};
|
};
|
||||||
|
@ -113,10 +110,11 @@ pub fn decode_extended_full_viewing_key(
|
||||||
/// 0xbc, 0xe5,
|
/// 0xbc, 0xe5,
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// let pa = PaymentAddress {
|
/// let pa = PaymentAddress::from_parts(
|
||||||
/// diversifier: Diversifier([0u8; 11]),
|
/// Diversifier([0u8; 11]),
|
||||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
/// };
|
/// )
|
||||||
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
|
/// encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa),
|
||||||
|
@ -124,10 +122,7 @@ pub fn decode_extended_full_viewing_key(
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String {
|
||||||
bech32_encode(hrp, |w| {
|
bech32_encode(hrp, |w| w.write_all(&addr.to_bytes()))
|
||||||
w.write_all(&addr.diversifier.0)?;
|
|
||||||
addr.pk_d.write(w)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
/// Decodes a [`PaymentAddress`] from a Bech32-encoded string.
|
||||||
|
@ -153,10 +148,11 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||||
/// 0xbc, 0xe5,
|
/// 0xbc, 0xe5,
|
||||||
/// ]);
|
/// ]);
|
||||||
///
|
///
|
||||||
/// let pa = PaymentAddress {
|
/// let pa = PaymentAddress::from_parts(
|
||||||
/// diversifier: Diversifier([0u8; 11]),
|
/// Diversifier([0u8; 11]),
|
||||||
/// pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
/// edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
/// };
|
/// )
|
||||||
|
/// .unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// decode_payment_address(
|
/// decode_payment_address(
|
||||||
|
@ -168,17 +164,13 @@ pub fn encode_payment_address(hrp: &str, addr: &PaymentAddress<Bls12>) -> String
|
||||||
/// ```
|
/// ```
|
||||||
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
|
pub fn decode_payment_address(hrp: &str, s: &str) -> Result<Option<PaymentAddress<Bls12>>, Error> {
|
||||||
bech32_decode(hrp, s, |data| {
|
bech32_decode(hrp, s, |data| {
|
||||||
let mut diversifier = Diversifier([0; 11]);
|
if data.len() != 43 {
|
||||||
diversifier.0.copy_from_slice(&data[0..11]);
|
|
||||||
// Check that the diversifier is valid
|
|
||||||
if diversifier.g_d::<Bls12>(&JUBJUB).is_none() {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
edwards::Point::<Bls12, _>::read(&data[11..], &JUBJUB)
|
let mut bytes = [0; 43];
|
||||||
.ok()?
|
bytes.copy_from_slice(&data);
|
||||||
.as_prime_order(&JUBJUB)
|
PaymentAddress::<Bls12>::from_bytes(&bytes, &JUBJUB)
|
||||||
.map(|pk_d| PaymentAddress { pk_d, diversifier })
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,10 +195,11 @@ mod tests {
|
||||||
0xbc, 0xe5,
|
0xbc, 0xe5,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let addr = PaymentAddress {
|
let addr = PaymentAddress::from_parts(
|
||||||
diversifier: Diversifier([0u8; 11]),
|
Diversifier([0u8; 11]),
|
||||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
};
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let encoded_main =
|
let encoded_main =
|
||||||
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
"zs1qqqqqqqqqqqqqqqqqrjq05nyfku05msvu49mawhg6kr0wwljahypwyk2h88z6975u563j8nfaxd";
|
||||||
|
@ -247,10 +240,11 @@ mod tests {
|
||||||
0xbc, 0xe5,
|
0xbc, 0xe5,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let addr = PaymentAddress {
|
let addr = PaymentAddress::from_parts(
|
||||||
diversifier: Diversifier([1u8; 11]),
|
Diversifier([1u8; 11]),
|
||||||
pk_d: edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
edwards::Point::<Bls12, _>::rand(rng, &JUBJUB).mul_by_cofactor(&JUBJUB),
|
||||||
};
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let encoded_main =
|
let encoded_main =
|
||||||
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
|
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr);
|
||||||
|
|
|
@ -232,10 +232,7 @@ fn prf_ock(
|
||||||
///
|
///
|
||||||
/// let diversifier = Diversifier([0; 11]);
|
/// let diversifier = Diversifier([0; 11]);
|
||||||
/// let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap();
|
/// let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap();
|
||||||
/// let to = PaymentAddress {
|
/// let to = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
|
||||||
/// pk_d,
|
|
||||||
/// diversifier,
|
|
||||||
/// };
|
|
||||||
/// let ovk = OutgoingViewingKey([0; 32]);
|
/// let ovk = OutgoingViewingKey([0; 32]);
|
||||||
///
|
///
|
||||||
/// let value = 1000;
|
/// let value = 1000;
|
||||||
|
@ -294,14 +291,14 @@ impl SaplingNoteEncryption {
|
||||||
|
|
||||||
/// Generates `encCiphertext` for this note.
|
/// Generates `encCiphertext` for this note.
|
||||||
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
|
pub fn encrypt_note_plaintext(&self) -> [u8; ENC_CIPHERTEXT_SIZE] {
|
||||||
let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d);
|
let shared_secret = sapling_ka_agree(&self.esk, self.to.pk_d());
|
||||||
let key = kdf_sapling(shared_secret, &self.epk);
|
let key = kdf_sapling(shared_secret, &self.epk);
|
||||||
|
|
||||||
// Note plaintext encoding is defined in section 5.5 of the Zcash Protocol
|
// Note plaintext encoding is defined in section 5.5 of the Zcash Protocol
|
||||||
// Specification.
|
// Specification.
|
||||||
let mut input = [0; NOTE_PLAINTEXT_SIZE];
|
let mut input = [0; NOTE_PLAINTEXT_SIZE];
|
||||||
input[0] = 1;
|
input[0] = 1;
|
||||||
input[1..12].copy_from_slice(&self.to.diversifier.0);
|
input[1..12].copy_from_slice(&self.to.diversifier().0);
|
||||||
(&mut input[12..20])
|
(&mut input[12..20])
|
||||||
.write_u64::<LittleEndian>(self.note.value)
|
.write_u64::<LittleEndian>(self.note.value)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -375,7 +372,7 @@ fn parse_note_plaintext_without_memo(
|
||||||
.g_d::<Bls12>(&JUBJUB)?
|
.g_d::<Bls12>(&JUBJUB)?
|
||||||
.mul(ivk.into_repr(), &JUBJUB);
|
.mul(ivk.into_repr(), &JUBJUB);
|
||||||
|
|
||||||
let to = PaymentAddress { pk_d, diversifier };
|
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
|
||||||
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
|
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
|
||||||
|
|
||||||
if note.cm(&JUBJUB) != *cmu {
|
if note.cm(&JUBJUB) != *cmu {
|
||||||
|
@ -535,7 +532,7 @@ pub fn try_sapling_output_recovery(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let to = PaymentAddress { pk_d, diversifier };
|
let to = PaymentAddress::from_parts(diversifier, pk_d)?;
|
||||||
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
|
let note = to.create_note(v, rcm, &JUBJUB).unwrap();
|
||||||
|
|
||||||
if note.cm(&JUBJUB) != *cmu {
|
if note.cm(&JUBJUB) != *cmu {
|
||||||
|
@ -698,10 +695,47 @@ mod tests {
|
||||||
[u8; ENC_CIPHERTEXT_SIZE],
|
[u8; ENC_CIPHERTEXT_SIZE],
|
||||||
[u8; OUT_CIPHERTEXT_SIZE],
|
[u8; OUT_CIPHERTEXT_SIZE],
|
||||||
) {
|
) {
|
||||||
let diversifier = Diversifier([0; 11]);
|
|
||||||
let ivk = Fs::random(&mut rng);
|
let ivk = Fs::random(&mut rng);
|
||||||
|
|
||||||
|
let (ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
|
||||||
|
random_enc_ciphertext_with(ivk, rng);
|
||||||
|
|
||||||
|
assert!(try_sapling_note_decryption(&ivk, &epk, &cmu, &enc_ciphertext).is_some());
|
||||||
|
assert!(try_sapling_compact_note_decryption(
|
||||||
|
&ivk,
|
||||||
|
&epk,
|
||||||
|
&cmu,
|
||||||
|
&enc_ciphertext[..COMPACT_NOTE_SIZE]
|
||||||
|
)
|
||||||
|
.is_some());
|
||||||
|
assert!(try_sapling_output_recovery(
|
||||||
|
&ovk,
|
||||||
|
&cv,
|
||||||
|
&cmu,
|
||||||
|
&epk,
|
||||||
|
&enc_ciphertext,
|
||||||
|
&out_ciphertext
|
||||||
|
)
|
||||||
|
.is_some());
|
||||||
|
|
||||||
|
(ovk, ivk, cv, cmu, epk, enc_ciphertext, out_ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_enc_ciphertext_with<R: RngCore + CryptoRng>(
|
||||||
|
ivk: Fs,
|
||||||
|
mut rng: &mut R,
|
||||||
|
) -> (
|
||||||
|
OutgoingViewingKey,
|
||||||
|
Fs,
|
||||||
|
edwards::Point<Bls12, Unknown>,
|
||||||
|
Fr,
|
||||||
|
edwards::Point<Bls12, PrimeOrder>,
|
||||||
|
[u8; ENC_CIPHERTEXT_SIZE],
|
||||||
|
[u8; OUT_CIPHERTEXT_SIZE],
|
||||||
|
) {
|
||||||
|
let diversifier = Diversifier([0; 11]);
|
||||||
let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap().mul(ivk, &JUBJUB);
|
let pk_d = diversifier.g_d::<Bls12>(&JUBJUB).unwrap().mul(ivk, &JUBJUB);
|
||||||
let pa = PaymentAddress { diversifier, pk_d };
|
let pa = PaymentAddress::from_parts_unchecked(diversifier, pk_d);
|
||||||
|
|
||||||
// Construct the value commitment for the proof instance
|
// Construct the value commitment for the proof instance
|
||||||
let value = 100;
|
let value = 100;
|
||||||
|
@ -722,24 +756,6 @@ mod tests {
|
||||||
let enc_ciphertext = ne.encrypt_note_plaintext();
|
let enc_ciphertext = ne.encrypt_note_plaintext();
|
||||||
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);
|
let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu);
|
||||||
|
|
||||||
assert!(try_sapling_note_decryption(&ivk, epk, &cmu, &enc_ciphertext).is_some());
|
|
||||||
assert!(try_sapling_compact_note_decryption(
|
|
||||||
&ivk,
|
|
||||||
epk,
|
|
||||||
&cmu,
|
|
||||||
&enc_ciphertext[..COMPACT_NOTE_SIZE]
|
|
||||||
)
|
|
||||||
.is_some());
|
|
||||||
assert!(try_sapling_output_recovery(
|
|
||||||
&ovk,
|
|
||||||
&cv,
|
|
||||||
&cmu,
|
|
||||||
&epk,
|
|
||||||
&enc_ciphertext,
|
|
||||||
&out_ciphertext
|
|
||||||
)
|
|
||||||
.is_some());
|
|
||||||
|
|
||||||
(
|
(
|
||||||
ovk,
|
ovk,
|
||||||
ivk,
|
ivk,
|
||||||
|
@ -1256,6 +1272,20 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recovery_with_invalid_pk_d() {
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
let ivk = Fs::zero();
|
||||||
|
let (ovk, _, cv, cmu, epk, enc_ciphertext, out_ciphertext) =
|
||||||
|
random_enc_ciphertext_with(ivk, &mut rng);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
try_sapling_output_recovery(&ovk, &cv, &cmu, &epk, &enc_ciphertext, &out_ciphertext),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vectors() {
|
fn test_vectors() {
|
||||||
let test_vectors = crate::test_vectors::note_encryption::make_test_vectors();
|
let test_vectors = crate::test_vectors::note_encryption::make_test_vectors();
|
||||||
|
@ -1317,10 +1347,7 @@ mod tests {
|
||||||
let ock = prf_ock(&ovk, &cv, &cmu, &epk);
|
let ock = prf_ock(&ovk, &cv, &cmu, &epk);
|
||||||
assert_eq!(ock.as_bytes(), tv.ock);
|
assert_eq!(ock.as_bytes(), tv.ock);
|
||||||
|
|
||||||
let to = PaymentAddress {
|
let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap();
|
||||||
pk_d,
|
|
||||||
diversifier: Diversifier(tv.default_d),
|
|
||||||
};
|
|
||||||
let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap();
|
let note = to.create_note(tv.v, rcm, &JUBJUB).unwrap();
|
||||||
assert_eq!(note.cm(&JUBJUB), cmu);
|
assert_eq!(note.cm(&JUBJUB), cmu);
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,10 @@ impl<E: JubjubEngine> ViewingKey<E> {
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
params: &E::Params,
|
params: &E::Params,
|
||||||
) -> Option<PaymentAddress<E>> {
|
) -> Option<PaymentAddress<E>> {
|
||||||
diversifier.g_d(params).map(|g_d| {
|
diversifier.g_d(params).and_then(|g_d| {
|
||||||
let pk_d = g_d.mul(self.ivk(), params);
|
let pk_d = g_d.mul(self.ivk(), params);
|
||||||
|
|
||||||
PaymentAddress { pk_d, diversifier }
|
PaymentAddress::from_parts(diversifier, pk_d)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,16 @@ impl Diversifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A Sapling payment address.
|
||||||
|
///
|
||||||
|
/// # Invariants
|
||||||
|
///
|
||||||
|
/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
|
||||||
|
/// and not the identity).
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PaymentAddress<E: JubjubEngine> {
|
pub struct PaymentAddress<E: JubjubEngine> {
|
||||||
pub pk_d: edwards::Point<E, PrimeOrder>,
|
pk_d: edwards::Point<E, PrimeOrder>,
|
||||||
pub diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
|
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
|
||||||
|
@ -131,6 +137,67 @@ impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> PaymentAddress<E> {
|
impl<E: JubjubEngine> PaymentAddress<E> {
|
||||||
|
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
|
||||||
|
///
|
||||||
|
/// Returns None if `pk_d` is the identity.
|
||||||
|
pub fn from_parts(
|
||||||
|
diversifier: Diversifier,
|
||||||
|
pk_d: edwards::Point<E, PrimeOrder>,
|
||||||
|
) -> Option<Self> {
|
||||||
|
if pk_d == edwards::Point::zero() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(PaymentAddress { pk_d, diversifier })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
|
||||||
|
///
|
||||||
|
/// Only for test code, as this explicitly bypasses the invariant.
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn from_parts_unchecked(
|
||||||
|
diversifier: Diversifier,
|
||||||
|
pk_d: edwards::Point<E, PrimeOrder>,
|
||||||
|
) -> Self {
|
||||||
|
PaymentAddress { pk_d, diversifier }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a PaymentAddress from bytes.
|
||||||
|
pub fn from_bytes(bytes: &[u8; 43], params: &E::Params) -> Option<Self> {
|
||||||
|
let diversifier = {
|
||||||
|
let mut tmp = [0; 11];
|
||||||
|
tmp.copy_from_slice(&bytes[0..11]);
|
||||||
|
Diversifier(tmp)
|
||||||
|
};
|
||||||
|
// Check that the diversifier is valid
|
||||||
|
if diversifier.g_d::<E>(params).is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
edwards::Point::<E, _>::read(&bytes[11..43], params)
|
||||||
|
.ok()?
|
||||||
|
.as_prime_order(params)
|
||||||
|
.and_then(|pk_d| PaymentAddress::from_parts(diversifier, pk_d))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the byte encoding of this `PaymentAddress`.
|
||||||
|
pub fn to_bytes(&self) -> [u8; 43] {
|
||||||
|
let mut bytes = [0; 43];
|
||||||
|
bytes[0..11].copy_from_slice(&self.diversifier.0);
|
||||||
|
self.pk_d.write(&mut bytes[11..]).unwrap();
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [`Diversifier`] for this `PaymentAddress`.
|
||||||
|
pub fn diversifier(&self) -> &Diversifier {
|
||||||
|
&self.diversifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `pk_d` for this `PaymentAddress`.
|
||||||
|
pub fn pk_d(&self) -> &edwards::Point<E, PrimeOrder> {
|
||||||
|
&self.pk_d
|
||||||
|
}
|
||||||
|
|
||||||
pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
|
pub fn g_d(&self, params: &E::Params) -> Option<edwards::Point<E, PrimeOrder>> {
|
||||||
self.diversifier.g_d(params)
|
self.diversifier.g_d(params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl SaplingOutput {
|
||||||
|
|
||||||
let note = Note {
|
let note = Note {
|
||||||
g_d,
|
g_d,
|
||||||
pk_d: to.pk_d.clone(),
|
pk_d: to.pk_d().clone(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
r: rcm,
|
r: rcm,
|
||||||
};
|
};
|
||||||
|
@ -344,10 +344,11 @@ impl<R: RngCore + CryptoRng> Builder<R> {
|
||||||
} else if !self.spends.is_empty() {
|
} else if !self.spends.is_empty() {
|
||||||
(
|
(
|
||||||
self.spends[0].extsk.expsk.ovk,
|
self.spends[0].extsk.expsk.ovk,
|
||||||
PaymentAddress {
|
PaymentAddress::from_parts(
|
||||||
diversifier: self.spends[0].diversifier,
|
self.spends[0].diversifier,
|
||||||
pk_d: self.spends[0].note.pk_d.clone(),
|
self.spends[0].note.pk_d.clone(),
|
||||||
},
|
)
|
||||||
|
.ok_or(Error::InvalidAddress)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::NoChangeAddress);
|
return Err(Error::NoChangeAddress);
|
||||||
|
@ -450,16 +451,16 @@ impl<R: RngCore + CryptoRng> Builder<R> {
|
||||||
(diversifier, g_d)
|
(diversifier, g_d)
|
||||||
};
|
};
|
||||||
|
|
||||||
let pk_d = {
|
let (pk_d, payment_address) = loop {
|
||||||
let dummy_ivk = Fs::random(&mut self.rng);
|
let dummy_ivk = Fs::random(&mut self.rng);
|
||||||
g_d.mul(dummy_ivk, &JUBJUB)
|
let pk_d = g_d.mul(dummy_ivk, &JUBJUB);
|
||||||
|
if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d.clone()) {
|
||||||
|
break (pk_d, addr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
PaymentAddress {
|
payment_address,
|
||||||
diversifier,
|
|
||||||
pk_d: pk_d.clone(),
|
|
||||||
},
|
|
||||||
Note {
|
Note {
|
||||||
g_d,
|
g_d,
|
||||||
pk_d,
|
pk_d,
|
||||||
|
@ -644,7 +645,7 @@ mod tests {
|
||||||
builder
|
builder
|
||||||
.add_sapling_spend(
|
.add_sapling_spend(
|
||||||
extsk.clone(),
|
extsk.clone(),
|
||||||
to.diversifier,
|
*to.diversifier(),
|
||||||
note1.clone(),
|
note1.clone(),
|
||||||
witness1.clone(),
|
witness1.clone(),
|
||||||
)
|
)
|
||||||
|
@ -683,10 +684,10 @@ mod tests {
|
||||||
{
|
{
|
||||||
let mut builder = Builder::new(0);
|
let mut builder = Builder::new(0);
|
||||||
builder
|
builder
|
||||||
.add_sapling_spend(extsk.clone(), to.diversifier, note1, witness1)
|
.add_sapling_spend(extsk.clone(), *to.diversifier(), note1, witness1)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
builder
|
builder
|
||||||
.add_sapling_spend(extsk, to.diversifier, note2, witness2)
|
.add_sapling_spend(extsk, *to.diversifier(), note2, witness2)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
builder
|
builder
|
||||||
.add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None)
|
.add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None)
|
||||||
|
|
|
@ -548,7 +548,7 @@ mod tests {
|
||||||
let (j_m, addr_m) = xsk_m.default_address().unwrap();
|
let (j_m, addr_m) = xsk_m.default_address().unwrap();
|
||||||
assert_eq!(j_m.0, [0; 11]);
|
assert_eq!(j_m.0, [0; 11]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
addr_m.diversifier.0,
|
addr_m.diversifier().0,
|
||||||
// Computed using this Rust implementation
|
// Computed using this Rust implementation
|
||||||
[59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
|
[59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
|
||||||
);
|
);
|
||||||
|
|
|
@ -464,7 +464,7 @@ impl<'a, E: JubjubEngine> Circuit<E> for Output<'a, E> {
|
||||||
// they would like.
|
// they would like.
|
||||||
{
|
{
|
||||||
// Just grab pk_d from the witness
|
// Just grab pk_d from the witness
|
||||||
let pk_d = self.payment_address.as_ref().map(|e| e.pk_d.to_xy());
|
let pk_d = self.payment_address.as_ref().map(|e| e.pk_d().to_xy());
|
||||||
|
|
||||||
// Witness the y-coordinate, encoded as little
|
// Witness the y-coordinate, encoded as little
|
||||||
// endian bits (to match the representation)
|
// endian bits (to match the representation)
|
||||||
|
@ -584,7 +584,7 @@ fn test_input_circuit_with_bls12_381() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let g_d = payment_address.diversifier.g_d(params).unwrap();
|
let g_d = payment_address.diversifier().g_d(params).unwrap();
|
||||||
let commitment_randomness = fs::Fs::random(rng);
|
let commitment_randomness = fs::Fs::random(rng);
|
||||||
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
|
let auth_path = vec![Some((Fr::random(rng), rng.next_u32() % 2 != 0)); tree_depth];
|
||||||
let ar = fs::Fs::random(rng);
|
let ar = fs::Fs::random(rng);
|
||||||
|
@ -595,7 +595,7 @@ fn test_input_circuit_with_bls12_381() {
|
||||||
let note = Note {
|
let note = Note {
|
||||||
value: value_commitment.value,
|
value: value_commitment.value,
|
||||||
g_d: g_d.clone(),
|
g_d: g_d.clone(),
|
||||||
pk_d: payment_address.pk_d.clone(),
|
pk_d: payment_address.pk_d().clone(),
|
||||||
r: commitment_randomness.clone(),
|
r: commitment_randomness.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl SaplingProvingContext {
|
||||||
g_d: diversifier
|
g_d: diversifier
|
||||||
.g_d::<Bls12>(params)
|
.g_d::<Bls12>(params)
|
||||||
.expect("was a valid diversifier before"),
|
.expect("was a valid diversifier before"),
|
||||||
pk_d: payment_address.pk_d.clone(),
|
pk_d: payment_address.pk_d().clone(),
|
||||||
r: rcm,
|
r: rcm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue