}
}
- /// 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."
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(),
k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch\
9zw97j25emudupq63nyw24cg27h2rspfj9srp".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
- .amount_pico_btc(2500000000)
+ .amount_milli_satoshis(250_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
dhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7k\
hhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
- .amount_pico_btc(20000000000)
+ .amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.description("coffee beans".to_string())
- .amount_pico_btc(20000000000)
+ .amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
.payment_secret(PaymentSecret([42; 32]))
.build_raw()
assert_eq!(Invoice::from_str(
"lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg"
), Err(ParseOrSemanticError::ParseError(ParseError::UnknownSiPrefix)));
+ assert_eq!(Invoice::from_str(
+ "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s"
+ ), Err(ParseOrSemanticError::SemanticError(SemanticError::ImpreciseAmount)));
}