[API BREAK] Remove Rng from Secp256k1 and associated code
The Rng was only used for key generation, and for BIP32 users not even then; thus hauling around a Rng is a waste of space in addition to causing a massive amount of syntactic noise. For example rust-bitcoin almost always uses `()` as the Rng; having `Secp256k1` default to a `Secp256k1<Fortuna>` then means even more syntactic noise, rather than less. Now key generation functions take a Rng as a parameter, and the rest can forget about having a Rng. This also means that the Secp256k1 context never needs a mutable reference and can be easily put into an Arc if so desired.
This commit is contained in:
parent
83823379e4
commit
fb75373b47
73
src/key.rs
73
src/key.rs
|
@ -46,7 +46,7 @@ enum PublicKeyData {
|
|||
Uncompressed([u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE])
|
||||
}
|
||||
|
||||
fn random_32_bytes<R:Rng>(rng: &mut R) -> [u8; 32] {
|
||||
fn random_32_bytes<R: Rng>(rng: &mut R) -> [u8; 32] {
|
||||
let mut ret = [0u8; 32];
|
||||
rng.fill_bytes(&mut ret);
|
||||
ret
|
||||
|
@ -55,11 +55,11 @@ fn random_32_bytes<R:Rng>(rng: &mut R) -> [u8; 32] {
|
|||
impl SecretKey {
|
||||
/// Creates a new random secret key
|
||||
#[inline]
|
||||
pub fn new<R: Rng>(secp: &mut Secp256k1<R>) -> SecretKey {
|
||||
let mut data = random_32_bytes(&mut secp.rng);
|
||||
pub fn new<R: Rng>(secp: &Secp256k1, rng: &mut R) -> SecretKey {
|
||||
let mut data = random_32_bytes(rng);
|
||||
unsafe {
|
||||
while ffi::secp256k1_ec_seckey_verify(secp.ctx, data.as_ptr()) == 0 {
|
||||
data = random_32_bytes(&mut secp.rng);
|
||||
data = random_32_bytes(rng);
|
||||
}
|
||||
}
|
||||
SecretKey(data)
|
||||
|
@ -67,7 +67,7 @@ impl SecretKey {
|
|||
|
||||
/// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key
|
||||
#[inline]
|
||||
pub fn from_slice<R>(secp: &Secp256k1<R>, data: &[u8])
|
||||
pub fn from_slice(secp: &Secp256k1, data: &[u8])
|
||||
-> Result<SecretKey, Error> {
|
||||
match data.len() {
|
||||
constants::SECRET_KEY_SIZE => {
|
||||
|
@ -88,9 +88,7 @@ impl SecretKey {
|
|||
|
||||
#[inline]
|
||||
/// Adds one secret key to another, modulo the curve order
|
||||
pub fn add_assign<R>(&mut self,
|
||||
secp: &Secp256k1<R>,
|
||||
other: &SecretKey)
|
||||
pub fn add_assign(&mut self, secp: &Secp256k1, other: &SecretKey)
|
||||
-> Result<(), Error> {
|
||||
unsafe {
|
||||
if ffi::secp256k1_ec_privkey_tweak_add(secp.ctx, self.as_mut_ptr(), other.as_ptr()) != 1 {
|
||||
|
@ -117,7 +115,7 @@ impl PublicKey {
|
|||
|
||||
/// Creates a new public key from a secret key.
|
||||
#[inline]
|
||||
pub fn from_secret_key<R>(secp: &Secp256k1<R>,
|
||||
pub fn from_secret_key(secp: &Secp256k1,
|
||||
sk: &SecretKey,
|
||||
compressed: bool)
|
||||
-> PublicKey {
|
||||
|
@ -139,7 +137,7 @@ impl PublicKey {
|
|||
|
||||
/// Creates a public key directly from a slice
|
||||
#[inline]
|
||||
pub fn from_slice<R>(secp: &Secp256k1<R>, data: &[u8])
|
||||
pub fn from_slice(secp: &Secp256k1, data: &[u8])
|
||||
-> Result<PublicKey, Error> {
|
||||
match data.len() {
|
||||
constants::COMPRESSED_PUBLIC_KEY_SIZE => {
|
||||
|
@ -216,9 +214,7 @@ impl PublicKey {
|
|||
|
||||
#[inline]
|
||||
/// Adds the pk corresponding to `other` to the pk `self` in place
|
||||
pub fn add_exp_assign<R>(&mut self,
|
||||
secp: &Secp256k1<R>,
|
||||
other: &SecretKey)
|
||||
pub fn add_exp_assign(&mut self, secp: &Secp256k1, other: &SecretKey)
|
||||
-> Result<(), Error> {
|
||||
unsafe {
|
||||
if ffi::secp256k1_ec_pubkey_tweak_add(secp.ctx, self.as_mut_ptr(),
|
||||
|
@ -473,11 +469,11 @@ mod test {
|
|||
use super::{PublicKey, SecretKey};
|
||||
use super::super::constants;
|
||||
|
||||
use rand::Rng;
|
||||
use rand::{Rng, thread_rng};
|
||||
|
||||
#[test]
|
||||
fn skey_from_slice() {
|
||||
let s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
let sk = SecretKey::from_slice(&s, &[1; 31]);
|
||||
assert_eq!(sk, Err(InvalidSecretKey));
|
||||
|
||||
|
@ -487,7 +483,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn pubkey_from_slice() {
|
||||
let s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
assert_eq!(PublicKey::from_slice(&s, &[]), Err(InvalidPublicKey));
|
||||
assert_eq!(PublicKey::from_slice(&s, &[1, 2, 3]), Err(InvalidPublicKey));
|
||||
|
||||
|
@ -502,20 +498,20 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn keypair_slice_round_trip() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let (sk1, pk1) = s.generate_keypair(true);
|
||||
let (sk1, pk1) = s.generate_keypair(&mut thread_rng(), true);
|
||||
assert_eq!(SecretKey::from_slice(&s, &sk1[..]), Ok(sk1));
|
||||
assert_eq!(PublicKey::from_slice(&s, &pk1[..]), Ok(pk1));
|
||||
|
||||
let (sk2, pk2) = s.generate_keypair(false);
|
||||
let (sk2, pk2) = s.generate_keypair(&mut thread_rng(), false);
|
||||
assert_eq!(SecretKey::from_slice(&s, &sk2[..]), Ok(sk2));
|
||||
assert_eq!(PublicKey::from_slice(&s, &pk2[..]), Ok(pk2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_secret_key() {
|
||||
let s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
// Zero
|
||||
assert_eq!(SecretKey::from_slice(&s, &[0; 32]), Err(InvalidSecretKey));
|
||||
// -1
|
||||
|
@ -589,12 +585,12 @@ mod test {
|
|||
})
|
||||
);
|
||||
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
for _ in 0..500 {
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
round_trip!(sk);
|
||||
round_trip!(pk);
|
||||
let (sk, pk) = s.generate_keypair(true);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), true);
|
||||
round_trip!(sk);
|
||||
round_trip!(pk);
|
||||
}
|
||||
|
@ -650,12 +646,12 @@ mod test {
|
|||
})
|
||||
);
|
||||
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
for _ in 0..500 {
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
round_trip!(sk);
|
||||
round_trip!(pk);
|
||||
let (sk, pk) = s.generate_keypair(true);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), true);
|
||||
round_trip!(sk);
|
||||
round_trip!(pk);
|
||||
}
|
||||
|
@ -688,15 +684,14 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
let mut s = Secp256k1::with_rng(BadRng(0xff));
|
||||
s.generate_keypair(false);
|
||||
let mut s = Secp256k1::with_rng(BadRng(0xff));
|
||||
s.generate_keypair(true);
|
||||
let s = Secp256k1::new();
|
||||
s.generate_keypair(&mut BadRng(0xff), false);
|
||||
s.generate_keypair(&mut BadRng(0xff), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pubkey_from_bad_slice() {
|
||||
let s = Secp256k1::new_deterministic();
|
||||
let s = Secp256k1::new();
|
||||
// Bad sizes
|
||||
assert_eq!(PublicKey::from_slice(&s, &[0; constants::COMPRESSED_PUBLIC_KEY_SIZE - 1]),
|
||||
Err(InvalidPublicKey));
|
||||
|
@ -724,26 +719,26 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
let mut s = Secp256k1::with_rng(DumbRng(0));
|
||||
let (sk1, pk1) = s.generate_keypair(false);
|
||||
let (sk2, pk2) = s.generate_keypair(true);
|
||||
let s = Secp256k1::new();
|
||||
let (sk1, pk1) = s.generate_keypair(&mut DumbRng(0), false);
|
||||
let (sk2, pk2) = s.generate_keypair(&mut DumbRng(0), true);
|
||||
|
||||
assert_eq!(&format!("{:?}", sk1),
|
||||
"SecretKey(0200000001000000040000000300000006000000050000000800000007000000)");
|
||||
assert_eq!(&format!("{:?}", pk1),
|
||||
"PublicKey(049510c48c265cefb3413be0e6b75beef02ebafcaf6634f962b27b4832abc4feec01bd8ff2e31057f7b7a244ed8c5ccd9781a63a6f607b40b493330cd159ecd5ce)");
|
||||
assert_eq!(&format!("{:?}", sk2),
|
||||
"SecretKey(0a000000090000000c0000000b0000000e0000000d000000100000000f000000)");
|
||||
"SecretKey(0200000001000000040000000300000006000000050000000800000007000000)");
|
||||
assert_eq!(&format!("{:?}", pk2),
|
||||
"PublicKey(024889f1f4a9407f8588b55358c2b392a6d9662872d5b9fff98b6f68c5e290a866)");
|
||||
"PublicKey(029510c48c265cefb3413be0e6b75beef02ebafcaf6634f962b27b4832abc4feec)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addition() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let (mut sk1, mut pk1) = s.generate_keypair(true);
|
||||
let (mut sk2, mut pk2) = s.generate_keypair(true);
|
||||
let (mut sk1, mut pk1) = s.generate_keypair(&mut thread_rng(), true);
|
||||
let (mut sk2, mut pk2) = s.generate_keypair(&mut thread_rng(), true);
|
||||
|
||||
assert_eq!(PublicKey::from_secret_key(&s, &sk1, true), pk1);
|
||||
assert!(sk1.add_assign(&s, &sk2).is_ok());
|
||||
|
|
136
src/lib.rs
136
src/lib.rs
|
@ -35,7 +35,6 @@
|
|||
#![deny(unused_mut)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate crypto;
|
||||
extern crate rustc_serialize as serialize;
|
||||
extern crate serde;
|
||||
#[cfg(test)] extern crate test;
|
||||
|
@ -44,11 +43,9 @@ extern crate libc;
|
|||
extern crate rand;
|
||||
|
||||
use std::intrinsics::copy_nonoverlapping;
|
||||
use std::{cmp, fmt, io, ops, ptr};
|
||||
use std::{cmp, fmt, ops, ptr};
|
||||
use libc::c_int;
|
||||
use rand::{OsRng, Rng, SeedableRng};
|
||||
|
||||
use crypto::fortuna::Fortuna;
|
||||
use rand::Rng;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -233,84 +230,55 @@ impl fmt::Display for Error {
|
|||
}
|
||||
|
||||
/// The secp256k1 engine, used to execute all signature operations
|
||||
pub struct Secp256k1<R = Fortuna> {
|
||||
ctx: ffi::Context,
|
||||
rng: R
|
||||
pub struct Secp256k1 {
|
||||
ctx: ffi::Context
|
||||
}
|
||||
|
||||
impl<R: Clone> Clone for Secp256k1<R> {
|
||||
fn clone(&self) -> Secp256k1<R> {
|
||||
impl Clone for Secp256k1 {
|
||||
fn clone(&self) -> Secp256k1 {
|
||||
Secp256k1 {
|
||||
ctx: unsafe { ffi::secp256k1_context_clone(self.ctx) },
|
||||
rng: self.rng.clone()
|
||||
ctx: unsafe { ffi::secp256k1_context_clone(self.ctx) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: PartialEq> PartialEq for Secp256k1<R> {
|
||||
fn eq(&self, other: &Secp256k1<R>) -> bool {
|
||||
// The contexts will always be "equal" in a functional sense
|
||||
self.rng == other.rng
|
||||
}
|
||||
impl PartialEq for Secp256k1 {
|
||||
// Contexts will always be "equal" in a functional sense
|
||||
fn eq(&self, _: &Secp256k1) -> bool { true }
|
||||
}
|
||||
impl<R: Eq> Eq for Secp256k1<R> { }
|
||||
impl Eq for Secp256k1 { }
|
||||
|
||||
impl<R: fmt::Debug> fmt::Debug for Secp256k1<R> {
|
||||
impl fmt::Debug for Secp256k1 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Secp256k1 {{ ctx: (secp256k1 context), rng: {:?} }}", self.rng)
|
||||
write!(f, "Secp256k1 {{ secp256k1 context }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Drop for Secp256k1<R> {
|
||||
impl Drop for Secp256k1 {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::secp256k1_context_destroy(self.ctx); }
|
||||
}
|
||||
}
|
||||
|
||||
impl Secp256k1<()> {
|
||||
/// Constructs a new secp256k1 engine without a RNG. This is
|
||||
/// useful for, e.g. BIP32 implementations, where all keys are
|
||||
/// computed externally to the secp256k1 engine. Note that if
|
||||
/// you try to use this for `SecretKey::new`, which generates
|
||||
/// a random key, it will panic.
|
||||
pub fn new_deterministic() -> Secp256k1<()> {
|
||||
Secp256k1::with_rng(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Secp256k1<Fortuna> {
|
||||
/// Constructs a new secp256k1 engine with the default key-generation Rng
|
||||
/// (a Fortuna seeded with randomness from the OS during `new`)
|
||||
pub fn new() -> io::Result<Secp256k1<Fortuna>> {
|
||||
let mut osrng = try!(OsRng::new());
|
||||
let mut seed = [0; 2048];
|
||||
osrng.fill_bytes(&mut seed);
|
||||
let rng: Fortuna = SeedableRng::from_seed(&seed[..]);
|
||||
Ok(Secp256k1::with_rng(rng))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Secp256k1<R> {
|
||||
/// Generates a random keypair. Convenience function for `key::SecretKey::new`
|
||||
/// and `key::PublicKey::from_secret_key`; call those functions directly for
|
||||
/// batch key generation.
|
||||
#[inline]
|
||||
pub fn generate_keypair(&mut self, compressed: bool)
|
||||
-> (key::SecretKey, key::PublicKey) {
|
||||
let sk = key::SecretKey::new(self);
|
||||
let pk = key::PublicKey::from_secret_key(self, &sk, compressed);
|
||||
(sk, pk)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Secp256k1<R> {
|
||||
/// Constructs a new secp256k1 engine with its key-generation RNG specified
|
||||
pub fn with_rng(rng: R) -> Secp256k1<R> {
|
||||
impl Secp256k1 {
|
||||
/// Creates a new Secp256k1 context
|
||||
pub fn new() -> Secp256k1 {
|
||||
let ctx = unsafe {
|
||||
ffi::secp256k1_context_create(ffi::SECP256K1_START_VERIFY |
|
||||
ffi::SECP256K1_START_SIGN)
|
||||
};
|
||||
Secp256k1 { ctx: ctx, rng: rng }
|
||||
Secp256k1 { ctx: ctx }
|
||||
}
|
||||
|
||||
/// Generates a random keypair. Convenience function for `key::SecretKey::new`
|
||||
/// and `key::PublicKey::from_secret_key`; call those functions directly for
|
||||
/// batch key generation.
|
||||
#[inline]
|
||||
pub fn generate_keypair<R: Rng>(&self, rng: &mut R, compressed: bool)
|
||||
-> (key::SecretKey, key::PublicKey) {
|
||||
let sk = key::SecretKey::new(self, rng);
|
||||
let pk = key::PublicKey::from_secret_key(self, &sk, compressed);
|
||||
(sk, pk)
|
||||
}
|
||||
|
||||
/// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce`
|
||||
|
@ -412,7 +380,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn invalid_pubkey() {
|
||||
let s = Secp256k1::new_deterministic();
|
||||
let s = Secp256k1::new();
|
||||
let sig = Signature::from_slice(&[0; 72]).unwrap();
|
||||
let pk = PublicKey::new(true);
|
||||
let mut msg = [0u8; 32];
|
||||
|
@ -424,9 +392,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn valid_pubkey_uncompressed() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let (_, pk) = s.generate_keypair(false);
|
||||
let (_, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
|
||||
let sig = Signature::from_slice(&[0; 72]).unwrap();
|
||||
let mut msg = [0u8; 32];
|
||||
|
@ -438,9 +406,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn valid_pubkey_compressed() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let (_, pk) = s.generate_keypair(true);
|
||||
let (_, pk) = s.generate_keypair(&mut thread_rng(), true);
|
||||
let sig = Signature::from_slice(&[0; 72]).unwrap();
|
||||
let mut msg = [0u8; 32];
|
||||
thread_rng().fill_bytes(&mut msg);
|
||||
|
@ -451,7 +419,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sign() {
|
||||
let s = Secp256k1::new_deterministic();
|
||||
let s = Secp256k1::new();
|
||||
let one = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
|
||||
|
||||
|
@ -473,14 +441,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sign_and_verify() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let mut msg = [0; 32];
|
||||
for _ in 0..100 {
|
||||
thread_rng().fill_bytes(&mut msg);
|
||||
let msg = Message::from_slice(&msg).unwrap();
|
||||
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
let sig = s.sign(&msg, &sk);
|
||||
assert_eq!(s.verify(&msg, &sig, &pk), Ok(()));
|
||||
}
|
||||
|
@ -488,13 +456,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sign_and_verify_fail() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let mut msg = [0u8; 32];
|
||||
thread_rng().fill_bytes(&mut msg);
|
||||
let msg = Message::from_slice(&msg).unwrap();
|
||||
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
|
||||
let sig = s.sign(&msg, &sk);
|
||||
let (sig_compact, recid) = s.sign_compact(&msg, &sk);
|
||||
|
@ -510,13 +478,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn sign_compact_with_recovery() {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let mut msg = [0u8; 32];
|
||||
thread_rng().fill_bytes(&mut msg);
|
||||
let msg = Message::from_slice(&msg).unwrap();
|
||||
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut thread_rng(), false);
|
||||
|
||||
let (sig, recid) = s.sign_compact(&msg, &sk);
|
||||
|
||||
|
@ -525,7 +493,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn bad_recovery() {
|
||||
let s = Secp256k1::new().unwrap();
|
||||
let s = Secp256k1::new();
|
||||
|
||||
let msg = Message::from_slice(&[0x55; 32]).unwrap();
|
||||
|
||||
|
@ -569,9 +537,15 @@ mod tests {
|
|||
|
||||
#[bench]
|
||||
pub fn generate_compressed(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
struct CounterRng(u32);
|
||||
impl Rng for CounterRng {
|
||||
fn next_u32(&mut self) -> u32 { self.0 += 1; self.0 }
|
||||
}
|
||||
|
||||
let s = Secp256k1::new();
|
||||
let mut r = CounterRng(0);
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(true);
|
||||
let (sk, pk) = s.generate_keypair(&mut r, true);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
});
|
||||
|
@ -579,9 +553,15 @@ mod tests {
|
|||
|
||||
#[bench]
|
||||
pub fn generate_uncompressed(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
struct CounterRng(u32);
|
||||
impl Rng for CounterRng {
|
||||
fn next_u32(&mut self) -> u32 { self.0 += 1; self.0 }
|
||||
}
|
||||
|
||||
let s = Secp256k1::new();
|
||||
let mut r = CounterRng(0);
|
||||
bh.iter( || {
|
||||
let (sk, pk) = s.generate_keypair(false);
|
||||
let (sk, pk) = s.generate_keypair(&mut r, false);
|
||||
black_box(sk);
|
||||
black_box(pk);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue