}
}
- /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
- pub fn amount_pico_btc(mut self, amount: u64) -> Self {
+ /// Sets the amount in millisatoshis. The optimal SI prefix is chosen automatically.
+ pub fn amount_milli_satoshis(mut self, amount_msat: u64) -> Self {
+ let amount = amount_msat * 10; // Invoices are denominated in "pico BTC"
let biggest_possible_si_prefix = SiPrefix::values_desc()
.iter()
.find(|prefix| amount % prefix.multiplier() == 0)
invoice.check_field_counts().expect("should be ensured by type signature of builder");
invoice.check_feature_bits().expect("should be ensured by type signature of builder");
+ invoice.check_amount().expect("should be ensured by type signature of builder");
Ok(invoice)
}
Ok(())
}
+ /// Check that amount is a whole number of millisatoshis
+ fn check_amount(&self) -> Result<(), SemanticError> {
+ if let Some(amount_pico_btc) = self.amount_pico_btc() {
+ if amount_pico_btc % 10 != 0 {
+ return Err(SemanticError::ImpreciseAmount);
+ }
+ }
+ Ok(())
+ }
+
/// Check that feature bits are set as required
fn check_feature_bits(&self) -> Result<(), SemanticError> {
// "If the payment_secret feature is set, MUST include exactly one s field."
match self.signed_invoice.recover_payee_pub_key() {
Err(secp256k1::Error::InvalidRecoveryId) =>
return Err(SemanticError::InvalidRecoveryId),
- Err(_) => panic!("no other error may occur"),
+ Err(secp256k1::Error::InvalidSignature) =>
+ return Err(SemanticError::InvalidSignature),
+ Err(e) => panic!("no other error may occur, got {:?}", e),
Ok(_) => {},
}
/// ```
/// use lightning_invoice::*;
///
- /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
- /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
- /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
- /// ky03ylcqca784w";
+ /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
+ /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
+ /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
+ /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
+ /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
+ /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
+ /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
+ /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
+ /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
+ /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
+ /// j5r6drg6k6zcqj0fcwg";
///
/// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
///
invoice.check_field_counts()?;
invoice.check_feature_bits()?;
invoice.check_signature()?;
+ invoice.check_amount()?;
Ok(invoice)
}
/// The invoice's signature is invalid
InvalidSignature,
+
+ /// The invoice's amount was not a whole number of millisatoshis
+ ImpreciseAmount,
}
impl Display for SemanticError {
SemanticError::InvalidFeatures => f.write_str("The invoice's features are invalid"),
SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
+ SemanticError::ImpreciseAmount => f.write_str("The invoice's amount was not a whole number of millisatoshis"),
}
}
}
.current_timestamp();
let invoice = builder.clone()
- .amount_pico_btc(15000)
+ .amount_milli_satoshis(1500)
.build_raw()
.unwrap();
let invoice = builder.clone()
- .amount_pico_btc(1500)
+ .amount_milli_satoshis(150)
.build_raw()
.unwrap();
]);
let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
- .amount_pico_btc(123)
+ .amount_milli_satoshis(123)
.timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
.payee_pub_key(public_key.clone())
.expiry_time(Duration::from_secs(54321))
assert!(invoice.check_signature().is_ok());
assert_eq!(invoice.tagged_fields().count(), 10);
- assert_eq!(invoice.amount_pico_btc(), Some(123));
+ assert_eq!(invoice.amount_pico_btc(), Some(1230));
assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
assert_eq!(
invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),