-
Notifications
You must be signed in to change notification settings - Fork 79
Persistable payjoin types #552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,4 +27,4 @@ impl Database { | |
| } | ||
|
|
||
| #[cfg(feature = "v2")] | ||
| mod v2; | ||
| pub(crate) mod v2; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| use std::fmt::Display; | ||
|
|
||
| /// Types that can generate their own keys for persistent storage | ||
| pub trait Value: serde::Serialize + serde::de::DeserializeOwned + Sized + Clone { | ||
| type Key: AsRef<[u8]> + Clone + Display; | ||
|
|
||
| /// Unique identifier for this persisted value | ||
| fn key(&self) -> Self::Key; | ||
| } | ||
|
|
||
| /// Implemented types that should be persisted by the application. | ||
| pub trait Persister<V: Value> { | ||
| type Token: From<V>; | ||
| type Error: std::error::Error + Send + Sync + 'static; | ||
|
|
||
| fn save(&mut self, value: V) -> Result<Self::Token, Self::Error>; | ||
| fn load(&self, token: Self::Token) -> Result<V, Self::Error>; | ||
| } | ||
|
|
||
| /// A key type that stores the value itself for no-op persistence | ||
| #[derive(Debug, Clone, serde::Serialize)] | ||
| pub struct NoopToken<V: Value>(V); | ||
|
|
||
| impl<V: Value> AsRef<[u8]> for NoopToken<V> { | ||
| fn as_ref(&self) -> &[u8] { | ||
| // Since this is a no-op implementation, we can return an empty slice | ||
| // as we never actually need to use the bytes | ||
| &[] | ||
| } | ||
| } | ||
|
|
||
| impl<'de, V: Value> serde::Deserialize<'de> for NoopToken<V> { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: serde::Deserializer<'de>, | ||
| { | ||
| Ok(NoopToken(V::deserialize(deserializer)?)) | ||
| } | ||
| } | ||
|
|
||
| impl<V: Value> Value for NoopToken<V> { | ||
| type Key = V::Key; | ||
|
|
||
| fn key(&self) -> Self::Key { self.0.key() } | ||
| } | ||
|
|
||
| /// A persister that does nothing but store values in memory | ||
| #[derive(Debug, Clone)] | ||
| pub struct NoopPersister; | ||
|
|
||
| impl<V: Value> From<V> for NoopToken<V> { | ||
| fn from(value: V) -> Self { NoopToken(value) } | ||
| } | ||
| impl<V: Value> Persister<V> for NoopPersister { | ||
| type Token = NoopToken<V>; | ||
| type Error = std::convert::Infallible; | ||
|
|
||
| fn save(&mut self, value: V) -> Result<Self::Token, Self::Error> { Ok(NoopToken(value)) } | ||
|
|
||
| fn load(&self, token: Self::Token) -> Result<V, Self::Error> { Ok(token.0) } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| //! Receive BIP 77 Payjoin v2 | ||
| use std::fmt::{self, Display}; | ||
| use std::str::FromStr; | ||
| use std::time::{Duration, SystemTime}; | ||
|
|
||
|
|
@@ -19,6 +20,7 @@ use super::{ | |
| use crate::hpke::{decrypt_message_a, encrypt_message_b, HpkeKeyPair, HpkePublicKey}; | ||
| use crate::ohttp::{ohttp_decapsulate, ohttp_encapsulate, OhttpEncapsulationError, OhttpKeys}; | ||
| use crate::output_substitution::OutputSubstitution; | ||
| use crate::persist::{self, Persister}; | ||
| use crate::receive::{parse_payload, InputPair}; | ||
| use crate::uri::ShortId; | ||
| use crate::{IntoUrl, IntoUrlError, Request}; | ||
|
|
@@ -71,12 +73,12 @@ fn subdir_path_from_pubkey(pubkey: &HpkePublicKey) -> ShortId { | |
|
|
||
| /// A payjoin V2 receiver, allowing for polled requests to the | ||
| /// payjoin directory and response processing. | ||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Receiver { | ||
| #[derive(Debug)] | ||
| pub struct NewReceiver { | ||
| context: SessionContext, | ||
| } | ||
|
|
||
| impl Receiver { | ||
| impl NewReceiver { | ||
| /// Creates a new `Receiver` with the provided parameters. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This docstring should be updated since it now creates a NewReceiver |
||
| /// | ||
| /// # Parameters | ||
|
|
@@ -96,7 +98,7 @@ impl Receiver { | |
| ohttp_keys: OhttpKeys, | ||
| expire_after: Option<Duration>, | ||
| ) -> Result<Self, IntoUrlError> { | ||
| Ok(Self { | ||
| let receiver = Self { | ||
| context: SessionContext { | ||
| address, | ||
| directory: directory.into_url()?, | ||
|
|
@@ -107,9 +109,53 @@ impl Receiver { | |
| s: HpkeKeyPair::gen_keypair(), | ||
| e: None, | ||
| }, | ||
| }) | ||
| }; | ||
| Ok(receiver) | ||
| } | ||
|
|
||
| pub fn persist<P: Persister<Receiver>>( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs docstring |
||
| &self, | ||
| persister: &mut P, | ||
| ) -> Result<P::Token, ImplementationError> { | ||
| let receiver = Receiver { context: self.context.clone() }; | ||
| Ok(persister.save(receiver)?) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub struct Receiver { | ||
| context: SessionContext, | ||
| } | ||
|
|
||
| /// Opaque key type for the receiver | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct ReceiverToken(ShortId); | ||
|
|
||
| impl Display for ReceiverToken { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } | ||
| } | ||
|
|
||
| impl From<Receiver> for ReceiverToken { | ||
| fn from(receiver: Receiver) -> Self { ReceiverToken(id(&receiver.context.s)) } | ||
| } | ||
|
|
||
| impl AsRef<[u8]> for ReceiverToken { | ||
| fn as_ref(&self) -> &[u8] { self.0.as_bytes() } | ||
| } | ||
|
|
||
| impl persist::Value for Receiver { | ||
| type Key = ReceiverToken; | ||
|
|
||
| fn key(&self) -> Self::Key { ReceiverToken(id(&self.context.s)) } | ||
| } | ||
|
Comment on lines
+130
to
+150
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this should live in |
||
|
|
||
| impl Receiver { | ||
| pub fn load<P: Persister<Receiver>>( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs docstring |
||
| token: P::Token, | ||
| persister: &P, | ||
| ) -> Result<Self, ImplementationError> { | ||
| persister.load(token).map_err(ImplementationError::from) | ||
| } | ||
| /// Extract an OHTTP Encapsulated HTTP GET request for the Original PSBT | ||
| pub fn extract_req( | ||
| &mut self, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this comment is meant to live with
Receiver,NewReceiveris the unpersisted variety whose only purpose is to be persisted