wip: schema addition and structure for delegation

This commit is contained in:
Greg Heartsfield 2022-10-09 08:04:50 -05:00
parent 274c61bb72
commit 43b738c434
4 changed files with 133 additions and 1 deletions

72
src/delegation.rs Normal file
View File

@ -0,0 +1,72 @@
//! Event parsing and validation
//use crate::error::Error::*;
//use crate::error::Result;
//use crate::utils::unix_time;
//use bitcoin_hashes::{sha256, Hash};
//use lazy_static::lazy_static;
//use secp256k1::{schnorr, Secp256k1, VerifyOnly, XOnlyPublicKey};
use serde::{Deserialize, Serialize};
//use serde_json::value::Value;
//use serde_json::Number;
//use std::collections::HashMap;
//use std::collections::HashSet;
//use std::str::FromStr;
//use tracing::{debug, info};
// This handles everything related to delegation, in particular the
// condition/rune parsing and logic.
// Conditions are poorly specified, so we will implement the minimum
// necessary for now.
// fields MUST be either "kind" or "created_at".
// operators supported are ">", "<", "=", "!".
// no operations on 'content' are supported.
// this allows constraints for:
// valid date ranges (valid from X->Y dates).
// specific kinds (publish kind=1,5)
// kind ranges (publish ephemeral events, kind>19999&kind<30001)
// for more complex scenarios (allow delegatee to publish ephemeral
// AND replacement events), it may be necessary to generate and use
// different condition strings, since we do not support grouping or
// "OR" logic.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub enum Field {
Kind,
CreatedAt,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub enum Operator {
LessThan,
GreaterThan,
Equals,
NotEquals,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub enum Value {
Number(u64),
List(Vec<Value>),
}
/// Parsed delegation statement
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct Delegation {
pub(crate) pubkey: String,
pub(crate) conditions: Vec<Condition>,
pub(crate) signature: String,
}
/// Parsed delegation condition
/// see https://github.com/nostr-protocol/nips/pull/28#pullrequestreview-1084903800
/// An example complex condition would be: kind=1,2,3&created_at<1665265999
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct Condition {
pub(crate) field: Field,
pub(crate) operator: Operator,
pub(crate) value: Value,
}

View File

@ -110,6 +110,16 @@ impl Event {
None None
} }
// is this event delegated (properly)?
// does the signature match, and are conditions valid?
pub fn is_delegated(&self) -> bool {
// is there a delegation tag?
let _delegation_tag = self.tag_values_by_name("delegation");
// delegation tags should have exactly 3 elements after the name (pubkey, condition, sig)
// try to construct a delegation object (
todo!();
}
/// Build an event tag index /// Build an event tag index
fn build_index(&mut self) { fn build_index(&mut self) {
// if there are no tags; just leave the index as None // if there are no tags; just leave the index as None
@ -388,6 +398,32 @@ mod tests {
assert_eq!(v, vec!["foo", "bar", "baz"]); assert_eq!(v, vec!["foo", "bar", "baz"]);
} }
#[test]
fn event_no_tag_select() {
let e = Event {
id: "999".to_owned(),
pubkey: "012345".to_owned(),
created_at: 501234,
kind: 1,
tags: vec![
vec!["j".to_owned(), "abc".to_owned()],
vec!["e".to_owned(), "foo".to_owned()],
vec!["e".to_owned(), "baz".to_owned()],
vec![
"p".to_owned(),
"aaaa".to_owned(),
"ws://example.com".to_owned(),
],
],
content: "this is a test".to_owned(),
sig: "abcde".to_owned(),
tagidx: None,
};
let v = e.tag_values_by_name("x");
// asking for tags that don't exist just returns zero-length vector
assert_eq!(v.len(), 0);
}
#[test] #[test]
fn event_canonical_with_tags() { fn event_canonical_with_tags() {
let e = Event { let e = Event {

View File

@ -2,6 +2,7 @@ pub mod close;
pub mod config; pub mod config;
pub mod conn; pub mod conn;
pub mod db; pub mod db;
pub mod delegation;
pub mod error; pub mod error;
pub mod event; pub mod event;
pub mod hexrange; pub mod hexrange;

View File

@ -20,7 +20,7 @@ pragma mmap_size = 536870912; -- 512MB of mmap
"##; "##;
/// Latest database version /// Latest database version
pub const DB_VERSION: usize = 6; pub const DB_VERSION: usize = 7;
/// Schema definition /// Schema definition
const INIT_SQL: &str = formatcp!( const INIT_SQL: &str = formatcp!(
@ -40,6 +40,7 @@ event_hash BLOB NOT NULL, -- 4-byte hash
first_seen INTEGER NOT NULL, -- when the event was first seen (not authored!) (seconds since 1970) first_seen INTEGER NOT NULL, -- when the event was first seen (not authored!) (seconds since 1970)
created_at INTEGER NOT NULL, -- when the event was authored created_at INTEGER NOT NULL, -- when the event was authored
author BLOB NOT NULL, -- author pubkey author BLOB NOT NULL, -- author pubkey
delegator BLOB, -- delegator pubkey (NIP-26)
kind INTEGER NOT NULL, -- event kind kind INTEGER NOT NULL, -- event kind
hidden INTEGER, -- relevant for queries hidden INTEGER, -- relevant for queries
content TEXT NOT NULL -- serialized json of event object content TEXT NOT NULL -- serialized json of event object
@ -152,6 +153,9 @@ pub fn upgrade_db(conn: &mut PooledConnection) -> Result<()> {
if curr_version == 5 { if curr_version == 5 {
curr_version = mig_5_to_6(conn)?; curr_version = mig_5_to_6(conn)?;
} }
if curr_version == 6 {
curr_version = mig_6_to_7(conn)?;
}
if curr_version == DB_VERSION { if curr_version == DB_VERSION {
info!( info!(
"All migration scripts completed successfully. Welcome to v{}.", "All migration scripts completed successfully. Welcome to v{}.",
@ -348,3 +352,22 @@ fn mig_5_to_6(conn: &mut PooledConnection) -> Result<usize> {
info!("vacuumed DB after tags rebuild in {:?}", start.elapsed()); info!("vacuumed DB after tags rebuild in {:?}", start.elapsed());
Ok(6) Ok(6)
} }
fn mig_6_to_7(conn: &mut PooledConnection) -> Result<usize> {
info!("database schema needs update from 6->7");
// only change is adding a hidden column to events.
let upgrade_sql = r##"
ALTER TABLE event ADD delegator BLOB;
PRAGMA user_version = 7;
"##;
match conn.execute_batch(upgrade_sql) {
Ok(()) => {
info!("database schema upgraded v6 -> v7");
}
Err(err) => {
error!("update failed: {}", err);
panic!("database could not be upgraded");
}
}
Ok(7)
}