Skip to content

Commit 0679a8c

Browse files
committed
s/PrimitiveError/FfiValidationError/g
Rename error type and variants for clarity
1 parent 9fb307d commit 0679a8c

9 files changed

Lines changed: 85 additions & 103 deletions

File tree

payjoin-ffi/dart/test/test_payjoin_integration_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ Future<payjoin.ReceiveSession?> process_receiver_proposal(
374374

375375
void main() {
376376
group('Test integration', () {
377-
test('Invalid primitives', () async {
377+
test('FFI validation', () async {
378378
final tooLargeAmount = 21000000 * 100000000 + 1;
379379
// Invalid outpoint should fail before amount checks.
380380
final txinInvalid = payjoin.PlainTxIn(
@@ -439,7 +439,7 @@ void main() {
439439

440440
expect(
441441
() => pjUri.setAmountSats(tooLargeAmount),
442-
throwsA(isA<payjoin.PrimitiveException>()),
442+
throwsA(isA<payjoin.FfiValidationException>()),
443443
);
444444
});
445445

payjoin-ffi/javascript/test/integration.test.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ async function processReceiverProposal(
450450
throw new Error(`Unknown receiver state`);
451451
}
452452

453-
function testInvalidPrimitives(): void {
453+
function testFfiValidation(): void {
454454
const tooLargeAmount = 21000000n * 100000000n + 1n;
455455

456456
// Invalid outpoint (txid too long) should fail before amount checks.
@@ -486,9 +486,13 @@ function testInvalidPrimitives(): void {
486486
sequence: 0,
487487
witness: [],
488488
});
489-
assert.throws(() => {
489+
try {
490490
new payjoin.InputPair(amountOverflowTxIn, psbtIn, undefined);
491-
}, /(Amount out of range|AmountOutOfRange)/);
491+
assert.fail("Expected AmountOutOfRange error");
492+
} catch (e) {
493+
const [inner] = payjoin.InputPairError.FfiValidation.getInner(e);
494+
assert.strictEqual(inner.tag, "AmountOutOfRange");
495+
}
492496

493497
// Oversized script_pubkey should fail.
494498
const hugeScript = new Uint8Array(10_001).fill(0x51).buffer;
@@ -501,9 +505,13 @@ function testInvalidPrimitives(): void {
501505
redeemScript: undefined,
502506
witnessScript: undefined,
503507
});
504-
assert.throws(() => {
508+
try {
505509
new payjoin.InputPair(amountOverflowTxIn, oversizedPsbtIn, undefined);
506-
}, /(ScriptTooLarge|script too large|InvalidPrimitive)/);
510+
assert.fail("Expected ScriptTooLarge error");
511+
} catch (e) {
512+
const [inner] = payjoin.InputPairError.FfiValidation.getInner(e);
513+
assert.strictEqual(inner.tag, "ScriptTooLarge");
514+
}
507515

508516
// Weight must be positive and <= block weight.
509517
const smallTxOut = payjoin.PlainTxOut.create({
@@ -515,13 +523,17 @@ function testInvalidPrimitives(): void {
515523
redeemScript: undefined,
516524
witnessScript: undefined,
517525
});
518-
assert.throws(() => {
526+
try {
519527
new payjoin.InputPair(
520528
amountOverflowTxIn,
521529
smallPsbtIn,
522530
payjoin.PlainWeight.create({ weightUnits: 0n }),
523531
);
524-
}, /(WeightOutOfRange|Weight out of range|InvalidPsbtInput|InvalidPrimitive)/);
532+
assert.fail("Expected WeightOutOfRange error");
533+
} catch (e) {
534+
const [inner] = payjoin.InputPairError.FfiValidation.getInner(e);
535+
assert.strictEqual(inner.tag, "WeightOutOfRange");
536+
}
525537

526538
const pjUri = payjoin.Uri.parse(
527539
"bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?amount=1&pj=https://example.com",
@@ -531,11 +543,11 @@ function testInvalidPrimitives(): void {
531543
new payjoin.SenderBuilder(psbt, pjUri).buildRecommended(
532544
18446744073709551615n,
533545
);
534-
}, /(Fee rate out of range|RuntimeError)/);
546+
}, /RuntimeError/);
535547

536548
assert.throws(() => {
537549
pjUri.setAmountSats(tooLargeAmount);
538-
}, /(Amount out of range|AmountOutOfRange)/);
550+
}, /AmountOutOfRange/);
539551
}
540552

541553
async function testIntegrationV2ToV2(): Promise<void> {
@@ -692,7 +704,7 @@ async function testIntegrationV2ToV2(): Promise<void> {
692704

693705
async function runTests(): Promise<void> {
694706
await uniffiInitAsync();
695-
testInvalidPrimitives();
707+
testFfiValidation();
696708
await testIntegrationV2ToV2();
697709
}
698710

payjoin-ffi/python/test/test_payjoin_integration_test.py

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ def setUpClass(cls):
5656
cls.receiver = cls.env.get_receiver()
5757
cls.sender = cls.env.get_sender()
5858

59-
async def test_invalid_primitives(self):
59+
async def test_ffi_validation(self):
6060
too_large_amount = 21_000_000 * 100_000_000 + 1
61-
# Invalid outpoint should fail before amount checks.
61+
62+
# Invalid outpoint (txid too long) should fail before amount checks.
6263
txin_invalid = PlainTxIn(
6364
previous_output=PlainOutPoint(txid="00" * 64, vout=0),
6465
script_sig=b"",
@@ -70,12 +71,11 @@ async def test_invalid_primitives(self):
7071
redeem_script=None,
7172
witness_script=None,
7273
)
73-
with self.assertRaises(InputPairError):
74+
with self.assertRaises(InputPairError.InvalidOutPoint):
7475
InputPair(txin=txin_invalid, psbtin=psbt_in_dummy, expected_weight=None)
7576

76-
# Valid outpoint hits amount overflow.
77+
# Valid outpoint hits amount overflow validation.
7778
txin = PlainTxIn(
78-
# valid 32-byte txid so we exercise amount overflow instead of outpoint parsing
7979
previous_output=PlainOutPoint(txid="00" * 32, vout=0),
8080
script_sig=b"",
8181
sequence=0,
@@ -89,15 +89,11 @@ async def test_invalid_primitives(self):
8989
redeem_script=None,
9090
witness_script=None,
9191
)
92-
amount_oob_variant = getattr(InputPairError, "AmountOutOfRange", InputPairError)
93-
with self.assertRaises(amount_oob_variant) as ctx:
92+
with self.assertRaises(InputPairError.FfiValidation) as ctx:
9493
InputPair(txin=txin, psbtin=psbt_in, expected_weight=None)
95-
# Cope with bindings that don't expose nested variants.
96-
self.assertIsInstance(ctx.exception, InputPairError)
97-
if amount_oob_variant is not InputPairError:
98-
self.assertIsInstance(ctx.exception, amount_oob_variant)
94+
self.assertIsInstance(ctx.exception[0], FfiValidationError.AmountOutOfRange)
9995

100-
# Use a real v2 payjoin URI from the receiver harness to avoid the v1 panic path.
96+
# SenderBuilder rejects fee rate overflow.
10197
receiver_address = json.loads(self.receiver.call("getnewaddress", []))
10298
services = TestServices.initialize()
10399
services.wait_for_services_ready()
@@ -108,26 +104,13 @@ async def test_invalid_primitives(self):
108104
receiver_address, directory, ohttp_keys, recv_persister
109105
).pj_uri()
110106

111-
sender_prim_variant = getattr(SenderInputError, "Primitive", SenderInputError)
112-
with self.assertRaises(sender_prim_variant) as ctx:
107+
with self.assertRaises(SenderInputError.FfiValidation) as ctx:
113108
SenderBuilder(original_psbt(), pj_uri).build_recommended(2**64 - 1)
114-
if sender_prim_variant is not SenderInputError:
115-
self.assertIsInstance(ctx.exception, sender_prim_variant)
116-
fee_rate_variant = getattr(PrimitiveError, "FeeRateOutOfRange", PrimitiveError)
117-
cause = ctx.exception.__cause__
118-
if cause is not None:
119-
self.assertIsInstance(cause, fee_rate_variant)
120-
else:
121-
self.assertIn("FeeRateOutOfRange", str(ctx.exception))
122-
123-
prim_amount_variant = getattr(
124-
PrimitiveError, "AmountOutOfRange", PrimitiveError
125-
)
126-
with self.assertRaises(prim_amount_variant) as ctx:
109+
self.assertIsInstance(ctx.exception[0], FfiValidationError.FeeRateOutOfRange)
110+
111+
# PjUri rejects amount out of range.
112+
with self.assertRaises(FfiValidationError.AmountOutOfRange):
127113
pj_uri.set_amount_sats(too_large_amount)
128-
self.assertIsInstance(ctx.exception, PrimitiveError)
129-
if prim_amount_variant is not PrimitiveError:
130-
self.assertIsInstance(ctx.exception, prim_amount_variant)
131114

132115
async def process_receiver_proposal(
133116
self,

payjoin-ffi/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl From<ImplementationError> for payjoin::ImplementationError {
2222
pub struct SerdeJsonError(#[from] serde_json::Error);
2323

2424
#[derive(Debug, thiserror::Error, uniffi::Error)]
25-
pub enum PrimitiveError {
25+
pub enum FfiValidationError {
2626
#[error("Amount out of range: {amount_sat} sats (max {max_sat})")]
2727
AmountOutOfRange { amount_sat: u64, max_sat: u64 },
2828
#[error("{field} script is empty")]

payjoin-ffi/src/receive/error.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::Arc;
22

33
use payjoin::receive;
44

5-
use crate::error::{ImplementationError, PrimitiveError};
5+
use crate::error::{FfiValidationError, ImplementationError};
66
use crate::uri::error::IntoUrlError;
77

88
/// The top-level error type for the payjoin receiver
@@ -179,7 +179,7 @@ pub enum OutputSubstitutionError {
179179
#[error(transparent)]
180180
Protocol(Arc<OutputSubstitutionProtocolError>),
181181
#[error(transparent)]
182-
Primitive(PrimitiveError),
182+
FfiValidation(FfiValidationError),
183183
}
184184

185185
impl From<receive::OutputSubstitutionError> for OutputSubstitutionError {
@@ -188,8 +188,8 @@ impl From<receive::OutputSubstitutionError> for OutputSubstitutionError {
188188
}
189189
}
190190

191-
impl From<PrimitiveError> for OutputSubstitutionError {
192-
fn from(value: PrimitiveError) -> Self { OutputSubstitutionError::Primitive(value) }
191+
impl From<FfiValidationError> for OutputSubstitutionError {
192+
fn from(value: FfiValidationError) -> Self { OutputSubstitutionError::FfiValidation(value) }
193193
}
194194

195195
/// Error that may occur when coin selection fails.
@@ -213,18 +213,12 @@ pub enum InputPairError {
213213
/// Provided outpoint could not be parsed.
214214
#[error("Invalid outpoint (txid={txid}, vout={vout})")]
215215
InvalidOutPoint { txid: String, vout: u32 },
216-
/// Amount exceeds allowed maximum.
217-
#[error("Amount out of range: {amount_sat} sats (max {max_sat})")]
218-
AmountOutOfRange { amount_sat: u64, max_sat: u64 },
219-
/// Weight must be positive and no more than a block.
220-
#[error("Weight out of range: {weight_units} wu (max {max_wu})")]
221-
WeightOutOfRange { weight_units: u64, max_wu: u64 },
222216
/// PSBT input failed validation in the core library.
223217
#[error("Invalid PSBT input: {0}")]
224218
InvalidPsbtInput(Arc<PsbtInputError>),
225-
/// Primitive input failed validation in the FFI layer.
226-
#[error("Invalid primitive input: {0}")]
227-
InvalidPrimitive(PrimitiveError),
219+
/// Input failed validation in the FFI layer.
220+
#[error("Invalid input: {0}")]
221+
FfiValidation(FfiValidationError),
228222
}
229223

230224
impl InputPairError {
@@ -233,16 +227,8 @@ impl InputPairError {
233227
}
234228
}
235229

236-
impl From<PrimitiveError> for InputPairError {
237-
fn from(value: PrimitiveError) -> Self {
238-
match value {
239-
PrimitiveError::AmountOutOfRange { amount_sat, max_sat } =>
240-
InputPairError::AmountOutOfRange { amount_sat, max_sat },
241-
PrimitiveError::WeightOutOfRange { weight_units, max_wu } =>
242-
InputPairError::WeightOutOfRange { weight_units, max_wu },
243-
other => InputPairError::InvalidPrimitive(other),
244-
}
245-
}
230+
impl From<FfiValidationError> for InputPairError {
231+
fn from(value: FfiValidationError) -> Self { InputPairError::FfiValidation(value) }
246232
}
247233

248234
/// Error that may occur when a receiver event log is replayed

payjoin-ffi/src/receive/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use payjoin::bitcoin::FeeRate;
1212
use payjoin::persist::{MaybeFatalTransition, NextStateTransition};
1313

1414
use crate::error::ForeignError;
15-
pub use crate::error::{ImplementationError, PrimitiveError, SerdeJsonError};
15+
pub use crate::error::{FfiValidationError, ImplementationError, SerdeJsonError};
1616
use crate::ohttp::OhttpKeys;
1717
use crate::receive::error::{ReceiverPersistedError, ReceiverReplayError};
1818
use crate::uri::error::FeeRateError;
@@ -275,7 +275,7 @@ pub struct PlainTxOut {
275275
}
276276

277277
impl PlainTxOut {
278-
fn into_core(self) -> Result<payjoin::bitcoin::TxOut, PrimitiveError> {
278+
fn into_core(self) -> Result<payjoin::bitcoin::TxOut, FfiValidationError> {
279279
let value = validate_amount_sat(self.value_sat)?;
280280
let script_pubkey = validate_script_vec("script_pubkey", self.script_pubkey, false)?;
281281
Ok(payjoin::bitcoin::TxOut { value, script_pubkey })
@@ -370,7 +370,7 @@ pub struct PlainWeight {
370370
}
371371

372372
impl PlainWeight {
373-
fn into_core(self) -> Result<payjoin::bitcoin::Weight, PrimitiveError> {
373+
fn into_core(self) -> Result<payjoin::bitcoin::Weight, FfiValidationError> {
374374
validate_weight_units(self.weight_units)
375375
}
376376
}
@@ -409,12 +409,12 @@ impl ReceiverBuilder {
409409
))
410410
}
411411

412-
pub fn with_amount(&self, amount_sats: u64) -> Result<Self, PrimitiveError> {
412+
pub fn with_amount(&self, amount_sats: u64) -> Result<Self, FfiValidationError> {
413413
let amount = validate_amount_sat(amount_sats)?;
414414
Ok(Self(self.0.clone().with_amount(amount)))
415415
}
416416

417-
pub fn with_expiration(&self, expiration: u64) -> Result<Self, PrimitiveError> {
417+
pub fn with_expiration(&self, expiration: u64) -> Result<Self, FfiValidationError> {
418418
let expiration = validate_expiration_secs(expiration)?;
419419
Ok(Self(self.0.clone().with_expiration(expiration)))
420420
}
@@ -638,7 +638,7 @@ impl UncheckedOriginalPayload {
638638
&self,
639639
min_fee_rate: Option<u64>,
640640
can_broadcast: Arc<dyn CanBroadcast>,
641-
) -> Result<UncheckedOriginalPayloadTransition, PrimitiveError> {
641+
) -> Result<UncheckedOriginalPayloadTransition, FfiValidationError> {
642642
let min_fee_rate = validate_fee_rate_sat_per_kwu_opt(min_fee_rate)?;
643643
Ok(UncheckedOriginalPayloadTransition(Arc::new(RwLock::new(Some(
644644
self.0.clone().check_broadcast_suitability(min_fee_rate, |transaction| {
@@ -1031,7 +1031,7 @@ impl WantsFeeRange {
10311031
&self,
10321032
min_fee_rate_sat_per_vb: Option<u64>,
10331033
max_effective_fee_rate_sat_per_vb: Option<u64>,
1034-
) -> Result<WantsFeeRangeTransition, PrimitiveError> {
1034+
) -> Result<WantsFeeRangeTransition, FfiValidationError> {
10351035
let min_fee_rate_sat_per_vb = validate_fee_rate_sat_per_vb_opt(min_fee_rate_sat_per_vb)?;
10361036
let max_effective_fee_rate_sat_per_vb =
10371037
validate_fee_rate_sat_per_vb_opt(max_effective_fee_rate_sat_per_vb)?;

payjoin-ffi/src/send/error.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::sync::Arc;
33
use payjoin::bitcoin::psbt::PsbtParseError as CorePsbtParseError;
44
use payjoin::send;
55

6-
use crate::error::{ImplementationError, PrimitiveError};
6+
use crate::error::{FfiValidationError, ImplementationError};
77

88
/// Error building a Sender from a SenderBuilder.
99
///
@@ -42,11 +42,11 @@ pub enum SenderInputError {
4242
#[error(transparent)]
4343
Build(Arc<BuildSenderError>),
4444
#[error(transparent)]
45-
Primitive(PrimitiveError),
45+
FfiValidation(FfiValidationError),
4646
}
4747

48-
impl From<PrimitiveError> for SenderInputError {
49-
fn from(value: PrimitiveError) -> Self { SenderInputError::Primitive(value) }
48+
impl From<FfiValidationError> for SenderInputError {
49+
fn from(value: FfiValidationError) -> Self { SenderInputError::FfiValidation(value) }
5050
}
5151

5252
/// Error returned when request could not be created.

payjoin-ffi/src/uri/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub use error::{PjNotSupported, PjParseError, UrlParseError};
55
use payjoin::bitcoin::address::NetworkChecked;
66
use payjoin::UriExt;
77

8-
use crate::error::PrimitiveError;
8+
use crate::error::FfiValidationError;
99
use crate::validation::validate_amount_sat;
1010

1111
pub mod error;
@@ -65,7 +65,7 @@ impl PjUri {
6565
pub fn amount_sats(&self) -> Option<u64> { self.0.clone().amount.map(|e| e.to_sat()) }
6666

6767
/// Sets the amount in sats and returns a new PjUri
68-
pub fn set_amount_sats(&self, amount_sats: u64) -> Result<Self, PrimitiveError> {
68+
pub fn set_amount_sats(&self, amount_sats: u64) -> Result<Self, FfiValidationError> {
6969
let mut uri = self.0.clone();
7070
let amount = validate_amount_sat(amount_sats)?;
7171
uri.amount = Some(amount);

0 commit comments

Comments
 (0)