@@ -480,8 +480,9 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
480
480
}
481
481
}
482
482
483
- /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
484
- pub fn amount_pico_btc ( mut self , amount : u64 ) -> Self {
483
+ /// Sets the amount in millisatoshis. The optimal SI prefix is choosen automatically.
484
+ pub fn amount_milli_satoshis ( mut self , amount_msat : u64 ) -> Self {
485
+ let amount = amount_msat * 10 ; // Invoices are denominated in "pico BTC"
485
486
let biggest_possible_si_prefix = SiPrefix :: values_desc ( )
486
487
. iter ( )
487
488
. find ( |prefix| amount % prefix. multiplier ( ) == 0 )
@@ -673,6 +674,7 @@ impl<S: tb::Bool> InvoiceBuilder<tb::True, tb::True, tb::True, tb::True, S> {
673
674
674
675
invoice. check_field_counts ( ) . expect ( "should be ensured by type signature of builder" ) ;
675
676
invoice. check_feature_bits ( ) . expect ( "should be ensured by type signature of builder" ) ;
677
+ invoice. check_amount ( ) . expect ( "should be ensured by type signature of builder" ) ;
676
678
677
679
Ok ( invoice)
678
680
}
@@ -1019,6 +1021,16 @@ impl Invoice {
1019
1021
Ok ( ( ) )
1020
1022
}
1021
1023
1024
+ /// Check that amount is a whole number of millisatoshis
1025
+ fn check_amount ( & self ) -> Result < ( ) , SemanticError > {
1026
+ if let Some ( amount_pico_btc) = self . amount_pico_btc ( ) {
1027
+ if amount_pico_btc % 10 != 0 {
1028
+ return Err ( SemanticError :: ImpreciseAmount ) ;
1029
+ }
1030
+ }
1031
+ Ok ( ( ) )
1032
+ }
1033
+
1022
1034
/// Check that feature bits are set as required
1023
1035
fn check_feature_bits ( & self ) -> Result < ( ) , SemanticError > {
1024
1036
// "If the payment_secret feature is set, MUST include exactly one s field."
@@ -1092,6 +1104,7 @@ impl Invoice {
1092
1104
invoice. check_field_counts ( ) ?;
1093
1105
invoice. check_feature_bits ( ) ?;
1094
1106
invoice. check_signature ( ) ?;
1107
+ invoice. check_amount ( ) ?;
1095
1108
1096
1109
Ok ( invoice)
1097
1110
}
@@ -1401,6 +1414,9 @@ pub enum SemanticError {
1401
1414
1402
1415
/// The invoice's signature is invalid
1403
1416
InvalidSignature ,
1417
+
1418
+ /// The invoice's amount was not a whole number of millisatoshis
1419
+ ImpreciseAmount ,
1404
1420
}
1405
1421
1406
1422
impl Display for SemanticError {
@@ -1414,6 +1430,7 @@ impl Display for SemanticError {
1414
1430
SemanticError :: InvalidFeatures => f. write_str ( "The invoice's features are invalid" ) ,
1415
1431
SemanticError :: InvalidRecoveryId => f. write_str ( "The recovery id doesn't fit the signature/pub key" ) ,
1416
1432
SemanticError :: InvalidSignature => f. write_str ( "The invoice's signature is invalid" ) ,
1433
+ SemanticError :: ImpreciseAmount => f. write_str ( "The invoice's amount was not a whole number of millisatoshis" ) ,
1417
1434
}
1418
1435
}
1419
1436
}
@@ -1663,7 +1680,7 @@ mod test {
1663
1680
. current_timestamp ( ) ;
1664
1681
1665
1682
let invoice = builder. clone ( )
1666
- . amount_pico_btc ( 15000 )
1683
+ . amount_milli_satoshis ( 1500 )
1667
1684
. build_raw ( )
1668
1685
. unwrap ( ) ;
1669
1686
@@ -1672,7 +1689,7 @@ mod test {
1672
1689
1673
1690
1674
1691
let invoice = builder. clone ( )
1675
- . amount_pico_btc ( 1500 )
1692
+ . amount_milli_satoshis ( 150 )
1676
1693
. build_raw ( )
1677
1694
. unwrap ( ) ;
1678
1695
@@ -1803,7 +1820,7 @@ mod test {
1803
1820
] ) ;
1804
1821
1805
1822
let builder = InvoiceBuilder :: new ( Currency :: BitcoinTestnet )
1806
- . amount_pico_btc ( 123 )
1823
+ . amount_milli_satoshis ( 123 )
1807
1824
. timestamp ( UNIX_EPOCH + Duration :: from_secs ( 1234567 ) )
1808
1825
. payee_pub_key ( public_key. clone ( ) )
1809
1826
. expiry_time ( Duration :: from_secs ( 54321 ) )
@@ -1823,7 +1840,7 @@ mod test {
1823
1840
assert ! ( invoice. check_signature( ) . is_ok( ) ) ;
1824
1841
assert_eq ! ( invoice. tagged_fields( ) . count( ) , 10 ) ;
1825
1842
1826
- assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 123 ) ) ;
1843
+ assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 1230 ) ) ;
1827
1844
assert_eq ! ( invoice. currency( ) , Currency :: BitcoinTestnet ) ;
1828
1845
assert_eq ! (
1829
1846
invoice. timestamp( ) . duration_since( UNIX_EPOCH ) . unwrap( ) . as_secs( ) ,
0 commit comments