Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions payjoin-cli/src/app/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use payjoin::receive::v2::{
WithContext,
};
use payjoin::receive::{Error, ReplyableError};
use payjoin::send::v2::{Sender, SenderBuilder};
use payjoin::send::v2::{Sender, SenderBuilder, WithReplyKey};
use payjoin::{ImplementationError, Uri};
use tokio::sync::watch;

Expand Down Expand Up @@ -137,7 +137,7 @@ impl AppTrait for App {

impl App {
#[allow(clippy::incompatible_msrv)]
async fn spawn_payjoin_sender(&self, mut req_ctx: Sender) -> Result<()> {
async fn spawn_payjoin_sender(&self, mut req_ctx: Sender<WithReplyKey>) -> Result<()> {
let mut interrupt = self.interrupt.clone();
tokio::select! {
res = self.long_poll_post(&mut req_ctx) => {
Expand Down Expand Up @@ -200,7 +200,7 @@ impl App {
Ok(())
}

async fn long_poll_post(&self, req_ctx: &mut Sender) -> Result<Psbt> {
async fn long_poll_post(&self, req_ctx: &mut Sender<WithReplyKey>) -> Result<Psbt> {
let ohttp_relay = self.unwrap_relay_or_else_fetch().await?;

match req_ctx.extract_v2(ohttp_relay.clone()) {
Expand Down
21 changes: 13 additions & 8 deletions payjoin-cli/src/db/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use bitcoincore_rpc::jsonrpc::serde_json;
use payjoin::persist::{Persister, Value};
use payjoin::receive::v2::{Receiver, ReceiverToken, WithContext};
use payjoin::send::v2::{Sender, SenderToken};
use payjoin::send::v2::{Sender, SenderToken, WithReplyKey};
use sled::Tree;
use url::Url;

Expand All @@ -14,10 +14,13 @@ impl SenderPersister {
pub fn new(db: Arc<Database>) -> Self { Self(db) }
}

impl Persister<Sender> for SenderPersister {
impl Persister<Sender<WithReplyKey>> for SenderPersister {
type Token = SenderToken;
type Error = crate::db::error::Error;
fn save(&mut self, value: Sender) -> std::result::Result<SenderToken, Self::Error> {
fn save(
&mut self,
value: Sender<WithReplyKey>,
) -> std::result::Result<SenderToken, Self::Error> {
let send_tree = self.0 .0.open_tree("send_sessions")?;
let key = value.key();
let value = serde_json::to_vec(&value).map_err(Error::Serialize)?;
Expand All @@ -26,7 +29,7 @@ impl Persister<Sender> for SenderPersister {
Ok(key)
}

fn load(&self, key: SenderToken) -> std::result::Result<Sender, Self::Error> {
fn load(&self, key: SenderToken) -> std::result::Result<Sender<WithReplyKey>, Self::Error> {
let send_tree = self.0 .0.open_tree("send_sessions")?;
let value = send_tree.get(key.as_ref())?.ok_or(Error::NotFound(key.to_string()))?;
serde_json::from_slice(&value).map_err(Error::Deserialize)
Expand Down Expand Up @@ -79,21 +82,23 @@ impl Database {
Ok(())
}

pub(crate) fn get_send_sessions(&self) -> Result<Vec<Sender>> {
pub(crate) fn get_send_sessions(&self) -> Result<Vec<Sender<WithReplyKey>>> {
let send_tree: Tree = self.0.open_tree("send_sessions")?;
let mut sessions = Vec::new();
for item in send_tree.iter() {
let (_, value) = item?;
let session: Sender = serde_json::from_slice(&value).map_err(Error::Deserialize)?;
let session: Sender<WithReplyKey> =
serde_json::from_slice(&value).map_err(Error::Deserialize)?;
sessions.push(session);
}
Ok(sessions)
}

pub(crate) fn get_send_session(&self, pj_url: &Url) -> Result<Option<Sender>> {
pub(crate) fn get_send_session(&self, pj_url: &Url) -> Result<Option<Sender<WithReplyKey>>> {
let send_tree = self.0.open_tree("send_sessions")?;
if let Some(val) = send_tree.get(pj_url.as_str())? {
let session: Sender = serde_json::from_slice(&val).map_err(Error::Deserialize)?;
let session: Sender<WithReplyKey> =
serde_json::from_slice(&val).map_err(Error::Deserialize)?;
Ok(Some(session))
} else {
Ok(None)
Expand Down
8 changes: 4 additions & 4 deletions payjoin-ffi/python/test/test_payjoin_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ def __init__(self):
super().__init__()
self.senders = {}

def save(self, sender: Sender) -> SenderToken:
def save(self, sender: WithReplyKey) -> SenderToken:
self.senders[str(sender.key())] = sender.to_json()
return sender.key()

def load(self, token: SenderToken) -> Sender:
def load(self, token: SenderToken) -> WithReplyKey:
token = str(token)
if token not in self.senders.keys():
raise ValueError(f"Token not found: {token}")
return Sender.from_json(self.senders[token])
return WithReplyKey.from_json(self.senders[token])

class TestPayjoin(unittest.IsolatedAsyncioTestCase):
@classmethod
Expand Down Expand Up @@ -106,7 +106,7 @@ async def test_integration_v2_to_v2(self):
new_sender = SenderBuilder(psbt, pj_uri).build_recommended(1000)
persister = InMemorySenderPersister()
token = new_sender.persist(persister)
req_ctx: Sender = Sender.load(token, persister)
req_ctx: WithReplyKey = WithReplyKey.load(token, persister)
request: RequestV2PostContext = req_ctx.extract_v2(ohttp_relay.as_string())
response = await agent.post(
url=request.request.url.as_string(),
Expand Down
8 changes: 4 additions & 4 deletions payjoin-ffi/python/test/test_payjoin_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ class InMemorySenderPersister(payjoin.payjoin_ffi.SenderPersister):
def __init__(self):
self.senders = {}

def save(self, sender: payjoin.Sender) -> payjoin.SenderToken:
def save(self, sender: payjoin.WithReplyKey) -> payjoin.SenderToken:
self.senders[str(sender.key())] = sender.to_json()
return sender.key()

def load(self, token: payjoin.SenderToken) -> payjoin.Sender:
def load(self, token: payjoin.SenderToken) -> payjoin.WithReplyKey:
token = str(token)
if token not in self.senders.keys():
raise ValueError(f"Token not found: {token}")
return payjoin.Sender.from_json(self.senders[token])
return payjoin.WithReplyKey.from_json(self.senders[token])

class TestSenderPersistence(unittest.TestCase):
def test_sender_persistence(self):
Expand All @@ -93,7 +93,7 @@ def test_sender_persistence(self):
psbt = "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA="
new_sender = payjoin.SenderBuilder(psbt, uri).build_recommended(1000)
token = new_sender.persist(persister)
payjoin.Sender.load(token, persister)
payjoin.WithReplyKey.load(token, persister)

if __name__ == "__main__":
unittest.main()
54 changes: 33 additions & 21 deletions payjoin-ffi/src/send/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl SenderBuilder {
/// Prepare an HTTP request and request context to process the response
///
/// Call [`SenderBuilder::build_recommended()`] or other `build` methods
/// to create a [`Sender`]
/// to create a [`WithReplyKey`]
pub fn new(psbt: String, uri: PjUri) -> Result<Self, BuildSenderError> {
let psbt = payjoin::bitcoin::psbt::Psbt::from_str(psbt.as_str())?;
Ok(payjoin::send::v2::SenderBuilder::new(psbt, uri.into()).into())
Expand Down Expand Up @@ -114,7 +114,7 @@ impl From<payjoin::send::v2::NewSender> for NewSender {
}

impl NewSender {
pub fn persist<P: Persister<payjoin::send::v2::Sender>>(
pub fn persist<P: Persister<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>>>(
&self,
persister: &mut P,
) -> Result<P::Token, ImplementationError> {
Expand All @@ -123,18 +123,20 @@ impl NewSender {
}

#[derive(Clone)]
pub struct Sender(payjoin::send::v2::Sender);
pub struct WithReplyKey(payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to my comment about WithContext in the receiver PR, I'd rather use a unique identifier for this state, especially as it gets its own type here in FFI, where it's not SenderWithReplyKey but just tacit WithReplyKey.

What do you think of Uninitialized/Initialized as NewSender/WithReplyKey renames?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I not exactly sure what you mean by "unique identifier for this state". The reason I went WithReplyKey is that is describes the diff between the current and last state.

FWIW we will need to introduce a Uninitalizsed sender state. Its used during session log replay.


impl From<payjoin::send::v2::Sender> for Sender {
fn from(value: payjoin::send::v2::Sender) -> Self { Self(value) }
impl From<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>> for WithReplyKey {
fn from(value: payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>) -> Self {
Self(value)
}
}

impl From<Sender> for payjoin::send::v2::Sender {
fn from(value: Sender) -> Self { value.0 }
impl From<WithReplyKey> for payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey> {
fn from(value: WithReplyKey) -> Self { value.0 }
}

impl Sender {
pub fn load<P: Persister<payjoin::send::v2::Sender>>(
impl WithReplyKey {
pub fn load<P: Persister<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>>>(
token: P::Token,
persister: &P,
) -> Result<Self, ImplementationError> {
Expand Down Expand Up @@ -164,7 +166,9 @@ impl Sender {
}

pub fn from_json(json: &str) -> Result<Self, SerdeJsonError> {
serde_json::from_str::<payjoin::send::v2::Sender>(json).map_err(Into::into).map(Into::into)
serde_json::from_str::<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>>(json)
.map_err(Into::into)
.map(Into::into)
}

pub fn key(&self) -> SenderToken { self.0.key() }
Expand All @@ -190,35 +194,43 @@ impl V1Context {
}
}

pub struct V2PostContext(Mutex<Option<payjoin::send::v2::V2PostContext>>);
pub struct V2PostContext(
Mutex<Option<payjoin::send::v2::Sender<payjoin::send::v2::V2PostContext>>>,
);

impl V2PostContext {
/// Decodes and validates the response.
/// Call this method with response from receiver to continue BIP-??? flow. A successful response can either be None if the relay has not response yet or Some(Psbt).
/// If the response is some valid PSBT you should sign and broadcast.
pub fn process_response(&self, response: &[u8]) -> Result<V2GetContext, EncapsulationError> {
<&V2PostContext as Into<payjoin::send::v2::V2PostContext>>::into(self)
.process_response(response)
.map(Into::into)
.map_err(Into::into)
<&V2PostContext as Into<payjoin::send::v2::Sender<payjoin::send::v2::V2PostContext>>>::into(
self,
)
.process_response(response)
.map(Into::into)
.map_err(Into::into)
}
}

impl From<&V2PostContext> for payjoin::send::v2::V2PostContext {
impl From<&V2PostContext> for payjoin::send::v2::Sender<payjoin::send::v2::V2PostContext> {
fn from(value: &V2PostContext) -> Self {
let mut data_guard = value.0.lock().unwrap();
Option::take(&mut *data_guard).expect("ContextV2 moved out of memory")
}
}

impl From<payjoin::send::v2::V2PostContext> for V2PostContext {
fn from(value: payjoin::send::v2::V2PostContext) -> Self { Self(Mutex::new(Some(value))) }
impl From<payjoin::send::v2::Sender<payjoin::send::v2::V2PostContext>> for V2PostContext {
fn from(value: payjoin::send::v2::Sender<payjoin::send::v2::V2PostContext>) -> Self {
Self(Mutex::new(Some(value)))
}
}

pub struct V2GetContext(payjoin::send::v2::V2GetContext);
pub struct V2GetContext(payjoin::send::v2::Sender<payjoin::send::v2::V2GetContext>);

impl From<payjoin::send::v2::V2GetContext> for V2GetContext {
fn from(value: payjoin::send::v2::V2GetContext) -> Self { Self(value) }
impl From<payjoin::send::v2::Sender<payjoin::send::v2::V2GetContext>> for V2GetContext {
fn from(value: payjoin::send::v2::Sender<payjoin::send::v2::V2GetContext>) -> Self {
Self(value)
}
}

impl V2GetContext {
Expand Down
44 changes: 27 additions & 17 deletions payjoin-ffi/src/send/uni.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl SenderBuilder {
/// Prepare an HTTP request and request context to process the response
///
/// Call [`SenderBuilder::build_recommended()`] or other `build` methods
/// to create a [`Sender`]
/// to create a [`WithReplyKey`]
#[uniffi::constructor]
pub fn new(psbt: String, uri: Arc<PjUri>) -> Result<Self, BuildSenderError> {
super::SenderBuilder::new(psbt, (*uri).clone()).map(Into::into)
Expand Down Expand Up @@ -107,24 +107,24 @@ impl NewSender {
}

#[derive(Clone, uniffi::Object)]
pub struct Sender(super::Sender);
pub struct WithReplyKey(super::WithReplyKey);

impl From<super::Sender> for Sender {
fn from(value: super::Sender) -> Self { Self(value) }
impl From<super::WithReplyKey> for WithReplyKey {
fn from(value: super::WithReplyKey) -> Self { Self(value) }
}

impl From<Sender> for super::Sender {
fn from(value: Sender) -> Self { value.0 }
impl From<WithReplyKey> for super::WithReplyKey {
fn from(value: WithReplyKey) -> Self { value.0 }
}

#[uniffi::export]
impl Sender {
impl WithReplyKey {
#[uniffi::constructor]
pub fn load(
token: Arc<SenderToken>,
persister: Arc<dyn SenderPersister>,
) -> Result<Self, ImplementationError> {
Ok(super::Sender::from(
Ok(super::WithReplyKey::from(
(*persister.load(token).map_err(|e| ImplementationError::from(e.to_string()))?).clone(),
)
.into())
Expand Down Expand Up @@ -154,7 +154,7 @@ impl Sender {

#[uniffi::constructor]
pub fn from_json(json: &str) -> Result<Self, SerdeJsonError> {
super::Sender::from_json(json).map(Into::into)
super::WithReplyKey::from_json(json).map(Into::into)
}

pub fn key(&self) -> SenderToken { self.0.key().into() }
Expand Down Expand Up @@ -248,8 +248,8 @@ impl V2GetContext {

#[uniffi::export(with_foreign)]
pub trait SenderPersister: Send + Sync {
fn save(&self, sender: Arc<Sender>) -> Result<Arc<SenderToken>, ForeignError>;
fn load(&self, token: Arc<SenderToken>) -> Result<Arc<Sender>, ForeignError>;
fn save(&self, sender: Arc<WithReplyKey>) -> Result<Arc<SenderToken>, ForeignError>;
fn load(&self, token: Arc<SenderToken>) -> Result<Arc<WithReplyKey>, ForeignError>;
}

// The adapter to use the save and load callbacks
Expand All @@ -262,16 +262,24 @@ impl CallbackPersisterAdapter {
}

// Implement the Persister trait for the adapter
impl payjoin::persist::Persister<payjoin::send::v2::Sender> for CallbackPersisterAdapter {
impl payjoin::persist::Persister<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>>
for CallbackPersisterAdapter
{
type Token = SenderToken; // Define the token type
type Error = ForeignError; // Define the error type

fn save(&mut self, sender: payjoin::send::v2::Sender) -> Result<Self::Token, Self::Error> {
let sender = Sender(super::Sender::from(sender));
fn save(
&mut self,
sender: payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>,
) -> Result<Self::Token, Self::Error> {
let sender = WithReplyKey(super::WithReplyKey::from(sender));
self.callback_persister.save(sender.into()).map(|token| (*token).clone())
}

fn load(&self, token: Self::Token) -> Result<payjoin::send::v2::Sender, Self::Error> {
fn load(
&self,
token: Self::Token,
) -> Result<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>, Self::Error> {
// Use the callback to load the sender
self.callback_persister.load(token.into()).map(|sender| (*sender).clone().0 .0)
}
Expand All @@ -285,8 +293,10 @@ impl std::fmt::Display for SenderToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) }
}

impl From<payjoin::send::v2::Sender> for SenderToken {
fn from(value: payjoin::send::v2::Sender) -> Self { SenderToken(value.into()) }
impl From<payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>> for SenderToken {
fn from(value: payjoin::send::v2::Sender<payjoin::send::v2::WithReplyKey>) -> Self {
SenderToken(value.into())
}
}

impl From<payjoin::send::v2::SenderToken> for SenderToken {
Expand Down
4 changes: 2 additions & 2 deletions payjoin-ffi/tests/bdk_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ mod v2 {
use bdk::wallet::AddressIndex;
use bitcoin_ffi::{Address, Network};
use payjoin_ffi::receive::{NewReceiver, PayjoinProposal, UncheckedProposal, WithContext};
use payjoin_ffi::send::{Sender, SenderBuilder};
use payjoin_ffi::send::{WithReplyKey, SenderBuilder};
use payjoin_ffi::uri::Uri;
use payjoin_ffi::{NoopPersister, Request};
use payjoin_test_utils::TestServices;
Expand Down Expand Up @@ -288,7 +288,7 @@ mod v2 {
let new_sender = SenderBuilder::new(psbt.to_string(), pj_uri)?
.build_recommended(payjoin::bitcoin::FeeRate::BROADCAST_MIN.to_sat_per_kwu())?;
let sender_token = new_sender.persist(&mut NoopPersister)?;
let req_ctx = Sender::load(sender_token, &NoopPersister)?;
let req_ctx = WithReplyKey::load(sender_token, &NoopPersister)?;
let (request, context) = req_ctx.extract_v2(ohttp_relay.to_owned().into())?;
let response = agent
.post(request.url.as_string())
Expand Down
Loading
Loading