feat(NIP-20): improve invalid event error messages

Instead of returning a NOTICE for invalid events, return a `OK false`
command result with a reason as to why the event is invalid.
This commit is contained in:
William Casarin 2022-11-11 07:20:36 -08:00 committed by Greg Heartsfield
parent 5a91419d34
commit 6d1244434b
3 changed files with 32 additions and 19 deletions

View File

@ -17,10 +17,16 @@ pub enum Error {
ConnWriteError, ConnWriteError,
#[error("EVENT parse failed")] #[error("EVENT parse failed")]
EventParseFailed, EventParseFailed,
#[error("ClOSE message parse failed")] #[error("CLOSE message parse failed")]
CloseParseFailed, CloseParseFailed,
#[error("Event validation failed")] #[error("Event invalid signature")]
EventInvalid, EventInvalidSignature,
#[error("Event invalid id")]
EventInvalidId,
#[error("Event malformed pubkey")]
EventMalformedPubkey,
#[error("Event could not canonicalize")]
EventCouldNotCanonicalize,
#[error("Event too large")] #[error("Event too large")]
EventMaxLengthError(usize), EventMaxLengthError(usize),
#[error("Subscription identifier max length exceeded")] #[error("Subscription identifier max length exceeded")]

View File

@ -27,6 +27,12 @@ pub struct EventCmd {
event: Event, event: Event,
} }
impl EventCmd {
pub fn event_id(&self) -> &str {
return &self.event.id;
}
}
/// Parsed nostr event. /// Parsed nostr event.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct Event { pub struct Event {
@ -83,13 +89,13 @@ impl From<EventCmd> for Result<Event> {
// ensure command is correct // ensure command is correct
if ec.cmd != "EVENT" { if ec.cmd != "EVENT" {
Err(CommandUnknownError) Err(CommandUnknownError)
} else if ec.event.is_valid() { } else {
ec.event.validate().map(|_| {
let mut e = ec.event; let mut e = ec.event;
e.build_index(); e.build_index();
e.update_delegation(); e.update_delegation();
Ok(e) e
} else { })
Err(EventInvalid)
} }
} }
} }
@ -220,7 +226,7 @@ impl Event {
} }
/// Check if this event has a valid signature. /// Check if this event has a valid signature.
fn is_valid(&self) -> bool { fn validate(&self) -> Result<()> {
// TODO: return a Result with a reason for invalid events // TODO: return a Result with a reason for invalid events
// validation is performed by: // validation is performed by:
// * parsing JSON string into event fields // * parsing JSON string into event fields
@ -229,8 +235,8 @@ impl Event {
// * serialize with no spaces/newlines // * serialize with no spaces/newlines
let c_opt = self.to_canonical(); let c_opt = self.to_canonical();
if c_opt.is_none() { if c_opt.is_none() {
debug!("event could not be canonicalized"); debug!("could not canonicalize");
return false; return Err(EventCouldNotCanonicalize);
} }
let c = c_opt.unwrap(); let c = c_opt.unwrap();
// * compute the sha256sum. // * compute the sha256sum.
@ -239,21 +245,21 @@ impl Event {
// * ensure the id matches the computed sha256sum. // * ensure the id matches the computed sha256sum.
if self.id != hex_digest { if self.id != hex_digest {
debug!("event id does not match digest"); debug!("event id does not match digest");
return false; return Err(EventInvalidId);
} }
// * validate the message digest (sig) using the pubkey & computed sha256 message hash. // * validate the message digest (sig) using the pubkey & computed sha256 message hash.
let sig = schnorr::Signature::from_str(&self.sig).unwrap(); let sig = schnorr::Signature::from_str(&self.sig).unwrap();
if let Ok(msg) = secp256k1::Message::from_slice(digest.as_ref()) { if let Ok(msg) = secp256k1::Message::from_slice(digest.as_ref()) {
if let Ok(pubkey) = XOnlyPublicKey::from_str(&self.pubkey) { if let Ok(pubkey) = XOnlyPublicKey::from_str(&self.pubkey) {
let verify = SECP.verify_schnorr(&sig, &msg, &pubkey); SECP.verify_schnorr(&sig, &msg, &pubkey)
matches!(verify, Ok(())) .map_err(|_| EventInvalidSignature)
} else { } else {
debug!("client sent malformed pubkey"); debug!("client sent malformed pubkey");
false Err(EventMalformedPubkey)
} }
} else { } else {
info!("error converting digest to secp256k1 message"); info!("error converting digest to secp256k1 message");
false Err(EventInvalidSignature)
} }
} }

View File

@ -572,6 +572,7 @@ async fn nostr_server(
Ok(NostrMessage::EventMsg(ec)) => { Ok(NostrMessage::EventMsg(ec)) => {
// An EventCmd needs to be validated to be converted into an Event // An EventCmd needs to be validated to be converted into an Event
// handle each type of message // handle each type of message
let evid = ec.event_id().to_owned();
let parsed : Result<Event> = Result::<Event>::from(ec); let parsed : Result<Event> = Result::<Event>::from(ec);
match parsed { match parsed {
Ok(e) => { Ok(e) => {
@ -592,9 +593,9 @@ async fn nostr_server(
} }
} }
}, },
Err(_) => { Err(e) => {
info!("client: {} sent an invalid event", cid); info!("client: {} sent an invalid event", cid);
ws_stream.send(make_notice_message(Notice::message("event was invalid".into()))).await.ok(); ws_stream.send(make_notice_message(Notice::invalid(evid, &format!("{}", e)))).await.ok();
} }
} }
}, },