Skip to content

Commit 069ad21

Browse files
authored
Save max fee rate in receiver session context (#1033)
2 parents 43a5a83 + e53b00b commit 069ad21

File tree

4 files changed

+56
-1
lines changed

4 files changed

+56
-1
lines changed

payjoin-cli/src/app/v2/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl AppTrait for App {
162162
let session =
163163
ReceiverBuilder::new(address, self.config.v2()?.pj_directory.clone(), ohttp_keys)?
164164
.with_amount(amount)
165+
.with_max_fee_rate(self.config.max_fee_rate.unwrap_or(FeeRate::BROADCAST_MIN))
165166
.build()
166167
.save(&persister)?;
167168

payjoin-ffi/src/receive/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::error::ForeignError;
1515
pub use crate::error::{ImplementationError, SerdeJsonError};
1616
use crate::ohttp::OhttpKeys;
1717
use crate::receive::error::{ReceiverPersistedError, ReceiverReplayError};
18-
use crate::uri::error::IntoUrlError;
18+
use crate::uri::error::{FeeRateError, IntoUrlError};
1919
use crate::{ClientResponse, OutputSubstitution, Request};
2020

2121
pub mod error;
@@ -274,6 +274,16 @@ impl ReceiverBuilder {
274274
Self(self.0.clone().with_expiry(Duration::from_secs(expiry)))
275275
}
276276

277+
/// Set the maximum effective fee rate the receiver is willing to pay for their own input/output contributions
278+
pub fn with_max_fee_rate(
279+
&self,
280+
max_effective_fee_rate_sat_per_vb: u64,
281+
) -> Result<Self, FeeRateError> {
282+
let fee_rate = bitcoin_ffi::FeeRate::from_sat_per_vb(max_effective_fee_rate_sat_per_vb)
283+
.map_err(FeeRateError::from)?;
284+
Ok(Self(self.0.clone().with_max_fee_rate(fee_rate.into())))
285+
}
286+
277287
pub fn build(&self) -> InitialReceiveTransition {
278288
InitialReceiveTransition(Arc::new(RwLock::new(Some(self.0.clone().build()))))
279289
}

payjoin-ffi/src/uri/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ pub struct UrlParseError(#[from] payjoin::ParseError);
2525
#[derive(Debug, thiserror::Error, uniffi::Object)]
2626
#[error(transparent)]
2727
pub struct IntoUrlError(#[from] payjoin::IntoUrlError);
28+
29+
#[derive(Debug, thiserror::Error, uniffi::Object)]
30+
#[error(transparent)]
31+
pub struct FeeRateError(#[from] bitcoin_ffi::error::FeeRateError);

payjoin/src/core/receive/v2/mod.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub struct SessionContext {
7373
amount: Option<Amount>,
7474
receiver_key: HpkeKeyPair,
7575
reply_key: Option<HpkePublicKey>,
76+
max_fee_rate: FeeRate,
7677
}
7778

7879
impl SessionContext {
@@ -299,6 +300,7 @@ impl ReceiverBuilder {
299300
amount: None,
300301
mailbox: None,
301302
reply_key: None,
303+
max_fee_rate: FeeRate::BROADCAST_MIN,
302304
};
303305
Ok(Self(session_context))
304306
}
@@ -315,6 +317,11 @@ impl ReceiverBuilder {
315317
Ok(Self(SessionContext { mailbox: Some(mailbox.into_url()?), ..self.0 }))
316318
}
317319

320+
/// Set the maximum effective fee rate the receiver is willing to pay for their own input/output contributions
321+
pub fn with_max_fee_rate(self, max_fee_rate: FeeRate) -> Self {
322+
Self(SessionContext { max_fee_rate, ..self.0 })
323+
}
324+
318325
pub fn build(self) -> NextStateTransition<SessionEvent, Receiver<Initialized>> {
319326
NextStateTransition::success(
320327
SessionEvent::Created(self.0.clone()),
@@ -908,6 +915,8 @@ impl Receiver<WantsFeeRange> {
908915
min_fee_rate: Option<FeeRate>,
909916
max_effective_fee_rate: Option<FeeRate>,
910917
) -> MaybeFatalTransition<SessionEvent, Receiver<ProvisionalProposal>, ProtocolError> {
918+
let max_effective_fee_rate =
919+
max_effective_fee_rate.or(Some(self.state.session_context.max_fee_rate));
911920
let psbt_context = match self
912921
.state
913922
.inner
@@ -1132,6 +1141,7 @@ pub mod test {
11321141
receiver_key: HpkeKeyPair::gen_keypair(),
11331142
reply_key: None,
11341143
amount: None,
1144+
max_fee_rate: FeeRate::BROADCAST_MIN,
11351145
});
11361146

11371147
pub(crate) fn unchecked_proposal_v2_from_test_vector() -> UncheckedOriginalPayload {
@@ -1396,6 +1406,36 @@ pub mod test {
13961406
}
13971407
}
13981408

1409+
#[test]
1410+
fn default_max_fee_rate() {
1411+
let noop_persister = NoopSessionPersister::default();
1412+
let receiver = ReceiverBuilder::new(
1413+
SHARED_CONTEXT.address.clone(),
1414+
SHARED_CONTEXT.directory.clone(),
1415+
SHARED_CONTEXT.ohttp_keys.clone(),
1416+
)
1417+
.expect("constructor on test vector should not fail")
1418+
.build()
1419+
.save(&noop_persister)
1420+
.expect("Noop persister shouldn't fail");
1421+
1422+
assert_eq!(receiver.context.max_fee_rate, FeeRate::BROADCAST_MIN);
1423+
1424+
let non_default_max_fee_rate =
1425+
FeeRate::from_sat_per_vb(1000).expect("Fee rate should be valid");
1426+
let receiver = ReceiverBuilder::new(
1427+
SHARED_CONTEXT.address.clone(),
1428+
SHARED_CONTEXT.directory.clone(),
1429+
SHARED_CONTEXT.ohttp_keys.clone(),
1430+
)
1431+
.expect("constructor on test vector should not fail")
1432+
.with_max_fee_rate(non_default_max_fee_rate)
1433+
.build()
1434+
.save(&noop_persister)
1435+
.expect("Noop persister shouldn't fail");
1436+
assert_eq!(receiver.context.max_fee_rate, non_default_max_fee_rate);
1437+
}
1438+
13991439
#[test]
14001440
fn build_receiver_with_non_default_expiry() {
14011441
let now = SystemTime::now();

0 commit comments

Comments
 (0)