Skip to content

Commit

Permalink
nostr: refactor PublicKey to use byte array internally
Browse files Browse the repository at this point in the history
Use `[u8; 32]` instead of `XOnlyPublicKey` to speedup `PublicKey` parsing. The `XOnlyPublicKey` is used only for event verification and encryption/decryption.

Signed-off-by: Yuki Kishimoto <[email protected]>
  • Loading branch information
yukibtc committed Jan 4, 2025
1 parent bd0848a commit d05ca49
Show file tree
Hide file tree
Showing 18 changed files with 186 additions and 85 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

### Changed

* nostr: refactor `PublicKey` to use byte array internally ([Yuki Kishimoto])
* pool: update `Error::WebSocket` variant inner type ([Yuki Kishimoto])

### Added
Expand Down
4 changes: 2 additions & 2 deletions bindings/nostr-sdk-ffi/src/protocol/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use crate::error::{NostrSdkError, Result};
/// **Important: use of a strong cryptographic hash function may be critical to security! Do NOT use
/// unless you understand cryptographical implications.**
#[uniffi::export]
pub fn generate_shared_key(secret_key: &SecretKey, public_key: &PublicKey) -> Vec<u8> {
util::generate_shared_key(secret_key.deref(), public_key.deref()).to_vec()
pub fn generate_shared_key(secret_key: &SecretKey, public_key: &PublicKey) -> Result<Vec<u8>> {
Ok(util::generate_shared_key(secret_key.deref(), public_key.deref())?.to_vec())
}

#[derive(Enum)]
Expand Down
9 changes: 7 additions & 2 deletions bindings/nostr-sdk-js/src/protocol/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ use std::ops::Deref;
use nostr_sdk::prelude::*;
use wasm_bindgen::prelude::*;

use crate::error::{into_err, Result};
use crate::protocol::key::{JsPublicKey, JsSecretKey};

/// Generate shared key
///
/// **Important: use of a strong cryptographic hash function may be critical to security! Do NOT use
/// unless you understand cryptographical implications.**
#[wasm_bindgen(js_name = generateSharedKey)]
pub fn generate_shared_key(secret_key: &JsSecretKey, public_key: &JsPublicKey) -> Vec<u8> {
util::generate_shared_key(secret_key.deref(), public_key.deref()).to_vec()
pub fn generate_shared_key(secret_key: &JsSecretKey, public_key: &JsPublicKey) -> Result<Vec<u8>> {
Ok(
util::generate_shared_key(secret_key.deref(), public_key.deref())
.map_err(into_err)?
.to_vec(),
)
}
15 changes: 3 additions & 12 deletions crates/nostr-database/src/flatbuffers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ pub enum Error {
FlatBuffer(InvalidFlatbuffer),
/// Tag error
Tag(tag::Error),
/// Key error
Key(key::Error),
/// Secp256k1 error
Secp256k1(secp256k1::Error),
/// Not found
Expand All @@ -43,7 +41,6 @@ impl fmt::Display for Error {
match self {
Self::FlatBuffer(e) => write!(f, "{e}"),
Self::Tag(e) => write!(f, "{e}"),
Self::Key(e) => write!(f, "{e}"),
Self::Secp256k1(e) => write!(f, "{e}"),
Self::NotFound => write!(f, "not found"),
}
Expand All @@ -62,12 +59,6 @@ impl From<tag::Error> for Error {
}
}

impl From<key::Error> for Error {
fn from(e: key::Error) -> Self {
Self::Key(e)
}
}

impl From<secp256k1::Error> for Error {
fn from(e: secp256k1::Error) -> Self {
Self::Secp256k1(e)
Expand Down Expand Up @@ -96,8 +87,8 @@ impl FlatBufferEncode for Event {
fn encode<'a>(&self, fbb: &'a mut FlatBufferBuilder) -> &'a [u8] {
fbb.reset();

let id = event_fbs::Fixed32Bytes::new(&self.id.to_bytes());
let pubkey = event_fbs::Fixed32Bytes::new(&self.pubkey.to_bytes());
let id = event_fbs::Fixed32Bytes::new(self.id.as_bytes());
let pubkey = event_fbs::Fixed32Bytes::new(self.pubkey.as_bytes());
let sig = event_fbs::Fixed64Bytes::new(self.sig.as_ref());
let tags = self
.tags
Expand Down Expand Up @@ -144,7 +135,7 @@ impl FlatBufferDecode for Event {

Ok(Self::new(
EventId::from_byte_array(ev.id().ok_or(Error::NotFound)?.0),
PublicKey::from_slice(&ev.pubkey().ok_or(Error::NotFound)?.0)?,
PublicKey::from_byte_array(ev.pubkey().ok_or(Error::NotFound)?.0),
Timestamp::from(ev.created_at()),
Kind::from(ev.kind() as u16),
tags,
Expand Down
14 changes: 7 additions & 7 deletions crates/nostr-lmdb/src/store/lmdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl Lmdb {

// Index by author and kind (with created_at and id)
let akc_index_key: Vec<u8> = index::make_akc_index_key(
&event.pubkey.to_bytes(),
event.pubkey.as_bytes(),
event.kind.as_u16(),
&event.created_at,
event.id.as_bytes(),
Expand All @@ -175,7 +175,7 @@ impl Lmdb {

// Index by author (with created_at and id)
let ac_index_key: Vec<u8> = index::make_ac_index_key(
&event.pubkey.to_bytes(),
event.pubkey.as_bytes(),
&event.created_at,
event.id.as_bytes(),
);
Expand All @@ -185,7 +185,7 @@ impl Lmdb {
if let (Some(tag_name), Some(tag_value)) = (tag.single_letter_tag(), tag.content()) {
// Index by author and tag (with created_at and id)
let atc_index_key: Vec<u8> = index::make_atc_index_key(
&event.pubkey.to_bytes(),
event.pubkey.as_bytes(),
&tag_name,
tag_value,
&event.created_at,
Expand Down Expand Up @@ -574,7 +574,7 @@ impl Lmdb {

let mut iter = self.akc_iter(
txn,
&author.to_bytes(),
author.as_bytes(),
kind.as_u16(),
Timestamp::min(),
Timestamp::max(),
Expand All @@ -599,7 +599,7 @@ impl Lmdb {

let iter = self.atc_iter(
txn,
&addr.public_key.to_bytes(),
addr.public_key.as_bytes(),
&SingleLetterTag::lowercase(Alphabet::D),
&addr.identifier,
&Timestamp::min(),
Expand Down Expand Up @@ -636,7 +636,7 @@ impl Lmdb {

let iter = self.akc_iter(
read_txn,
&coordinate.public_key.to_bytes(),
coordinate.public_key.as_bytes(),
coordinate.kind.as_u16(),
Timestamp::zero(),
until,
Expand Down Expand Up @@ -668,7 +668,7 @@ impl Lmdb {

let iter = self.atc_iter(
read_txn,
&coordinate.public_key.to_bytes(),
coordinate.public_key.as_bytes(),
&SingleLetterTag::lowercase(Alphabet::D),
&coordinate.identifier,
&Timestamp::min(),
Expand Down
2 changes: 1 addition & 1 deletion crates/nostr-lmdb/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl Store {
for id in event.tags.event_ids() {
if let Some(target) = db.get_event_by_id(read_txn, id.as_bytes())? {
// Author must match
if target.author() != &event.pubkey.to_bytes() {
if target.author() != event.pubkey.as_bytes() {
return Ok(true);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/nostr-lmdb/src/store/types/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl From<Filter> for DatabaseFilter {
.map(|authors| {
authors
.into_iter()
.map(|pubkey| Fixed32Bytes::new(&pubkey.to_bytes()))
.map(|pubkey| Fixed32Bytes::new(pubkey.as_bytes()))
.collect()
})
.unwrap_or_default(),
Expand Down
4 changes: 2 additions & 2 deletions crates/nostr-ndb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ fn ndb_filter_conversion(f: Filter) -> nostrdb::Filter {

if let Some(authors) = f.authors {
if !authors.is_empty() {
let authors: Vec<[u8; 32]> = authors.into_iter().map(|p| p.serialize()).collect();
let authors: Vec<[u8; 32]> = authors.into_iter().map(|p| p.to_bytes()).collect();
filter = filter.authors(authors.iter());
}
}
Expand Down Expand Up @@ -221,7 +221,7 @@ fn ndb_filter_conversion(f: Filter) -> nostrdb::Filter {

fn ndb_note_to_event(note: Note) -> Result<Event, DatabaseError> {
let id = EventId::from_byte_array(*note.id());
let public_key = PublicKey::from_slice(note.pubkey()).map_err(DatabaseError::backend)?;
let public_key = PublicKey::from_byte_array(*note.pubkey());
let sig = Signature::from_slice(note.sig()).map_err(DatabaseError::backend)?;

let tags: Vec<Tag> = ndb_note_to_tags(&note)?;
Expand Down
8 changes: 6 additions & 2 deletions crates/nostr/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,12 @@ impl Event {
C: Verification,
{
let message: Message = Message::from_digest(self.id.to_bytes());
secp.verify_schnorr(&self.sig, &message, &self.pubkey)
.is_ok()
match self.pubkey.xonly() {
Ok(public_key) => secp
.verify_schnorr(&self.sig, &message, &public_key)
.is_ok(),
Err(..) => false, // TODO: return error?
}
}

/// Check POW
Expand Down
10 changes: 10 additions & 0 deletions crates/nostr/src/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub use self::public_key::PublicKey;
pub use self::secret_key::SecretKey;
#[cfg(feature = "std")]
use crate::signer::{NostrSigner, SignerBackend, SignerError};
use crate::util::hex;
#[cfg(feature = "std")]
use crate::{Event, UnsignedEvent, SECP256K1};

Expand All @@ -40,6 +41,8 @@ use crate::{Event, UnsignedEvent, SECP256K1};
pub enum Error {
/// Secp256k1 error
Secp256k1(secp256k1::Error),
/// Hex decode error
Hex(hex::Error),
/// Invalid secret key
InvalidSecretKey,
/// Invalid public key
Expand All @@ -55,6 +58,7 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Secp256k1(e) => write!(f, "{e}"),
Self::Hex(e) => write!(f, "{e}"),
Self::InvalidSecretKey => write!(f, "Invalid secret key"),
Self::InvalidPublicKey => write!(f, "Invalid public key"),
Self::InvalidChar(c) => write!(f, "Unsupported char: {c}"),
Expand All @@ -68,6 +72,12 @@ impl From<secp256k1::Error> for Error {
}
}

impl From<hex::Error> for Error {
fn from(e: hex::Error) -> Self {
Self::Hex(e)
}
}

/// Nostr keys
#[derive(Clone)]
pub struct Keys {
Expand Down
Loading

0 comments on commit d05ca49

Please sign in to comment.