Skip to content

Commit 8386e69

Browse files
committed
tweak invoice rpc for payment hash and preimage
1 parent 8140ee2 commit 8386e69

File tree

9 files changed

+111
-122
lines changed

9 files changed

+111
-122
lines changed

crates/fiber-lib/src/fiber/tests/mpp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2997,7 +2997,7 @@ async fn test_send_mpp_with_generated_invoice() {
29972997
let too_large_amount_invoice = nodes[1]
29982998
.gen_invoice(NewInvoiceParams {
29992999
amount: 20000000001,
3000-
payment_preimage: gen_rand_sha256_hash(),
3000+
payment_preimage: Some(gen_rand_sha256_hash()),
30013001
allow_mpp: Some(true),
30023002
..Default::default()
30033003
})
@@ -3017,7 +3017,7 @@ async fn test_send_mpp_with_generated_invoice() {
30173017
let ok_invoice = nodes[1]
30183018
.gen_invoice(NewInvoiceParams {
30193019
amount: 20000000000,
3020-
payment_preimage: gen_rand_sha256_hash(),
3020+
payment_preimage: Some(gen_rand_sha256_hash()),
30213021
allow_mpp: Some(true),
30223022
..Default::default()
30233023
})

crates/fiber-lib/src/fiber/tests/rpc.rs

Lines changed: 54 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ fn rpc_config_with_auth() -> (RpcConfig, KeyPair) {
2929
(config, root)
3030
}
3131

32-
#[tokio::test]
33-
async fn test_rpc_basic() {
34-
let (nodes, _channels) = create_n_nodes_network_with_params(
32+
async fn gen_mock_network() -> (Vec<NetworkNode>, Vec<Hash256>) {
33+
create_n_nodes_network_with_params(
3534
&[
3635
(
3736
(0, 1),
@@ -55,7 +54,13 @@ async fn test_rpc_basic() {
5554
2,
5655
Some(gen_rpc_config()),
5756
)
58-
.await;
57+
.await
58+
}
59+
60+
#[tokio::test]
61+
async fn test_rpc_basic() {
62+
let (nodes, _channels) = gen_mock_network().await;
63+
5964
let [node_0, node_1] = nodes.try_into().expect("2 nodes");
6065

6166
let res: ListChannelsResult = node_0
@@ -93,7 +98,8 @@ async fn test_rpc_basic() {
9398
fallback_address: None,
9499
final_expiry_delta: Some(900000 + 1234),
95100
udt_type_script: Some(Script::default().into()),
96-
payment_preimage: Hash256::default(),
101+
payment_preimage: Some(Hash256::default()),
102+
payment_hash: None,
97103
hash_algorithm: Some(crate::fiber::hash_algorithm::HashAlgorithm::CkbHash),
98104
allow_mpp: Some(true),
99105
atomic_mpp: None,
@@ -145,7 +151,8 @@ async fn test_rpc_basic() {
145151
fallback_address: None,
146152
final_expiry_delta: Some(900000 + 1234),
147153
udt_type_script: Some(Script::default().into()),
148-
payment_preimage: gen_rand_sha256_hash(),
154+
payment_preimage: Some(gen_rand_sha256_hash()),
155+
payment_hash: None,
149156
hash_algorithm: Some(crate::fiber::hash_algorithm::HashAlgorithm::CkbHash),
150157
allow_mpp: Some(false),
151158
atomic_mpp: None,
@@ -161,6 +168,40 @@ async fn test_rpc_basic() {
161168
assert!(internal_ckb_invoice.payment_secret().is_none());
162169
}
163170

171+
#[tokio::test]
172+
async fn test_invoice_rpc() {
173+
let (nodes, _channels) = gen_mock_network().await;
174+
175+
let [node_0, _node_1] = nodes.try_into().expect("2 nodes");
176+
177+
let payment_hash = gen_rand_sha256_hash();
178+
let new_invoice_params = NewInvoiceParams {
179+
amount: 1000,
180+
description: Some("test".to_string()),
181+
currency: Currency::Fibd,
182+
expiry: Some(322),
183+
fallback_address: None,
184+
final_expiry_delta: Some(900000 + 1234),
185+
udt_type_script: Some(Script::default().into()),
186+
payment_preimage: None,
187+
payment_hash: Some(payment_hash),
188+
hash_algorithm: Some(crate::fiber::hash_algorithm::HashAlgorithm::CkbHash),
189+
allow_mpp: None,
190+
atomic_mpp: Some(true),
191+
};
192+
193+
// node0 generate a invoice
194+
let invoice_res: InvoiceResult = node_0
195+
.send_rpc_request("new_invoice", new_invoice_params)
196+
.await
197+
.unwrap();
198+
199+
let ckb_invoice = invoice_res.invoice.clone();
200+
let invoice_payment_hash = ckb_invoice.data.payment_hash;
201+
assert_eq!(invoice_payment_hash, payment_hash);
202+
assert!(node_0.get_payment_preimage(&payment_hash).is_none());
203+
}
204+
164205
#[tokio::test]
165206
async fn test_rpc_list_peers() {
166207
let (nodes, _channels) = create_n_nodes_network_with_params(
@@ -239,31 +280,7 @@ async fn test_rpc_list_peers() {
239280

240281
#[tokio::test]
241282
async fn test_rpc_graph() {
242-
let (nodes, _channels) = create_n_nodes_network_with_params(
243-
&[
244-
(
245-
(0, 1),
246-
ChannelParameters {
247-
public: true,
248-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
249-
node_b_funding_amount: MIN_RESERVED_CKB,
250-
..Default::default()
251-
},
252-
),
253-
(
254-
(0, 1),
255-
ChannelParameters {
256-
public: true,
257-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
258-
node_b_funding_amount: MIN_RESERVED_CKB,
259-
..Default::default()
260-
},
261-
),
262-
],
263-
2,
264-
Some(gen_rpc_config()),
265-
)
266-
.await;
283+
let (nodes, _channels) = gen_mock_network().await;
267284
let [node_0, node_1] = nodes.try_into().expect("2 nodes");
268285

269286
let graph_nodes: GraphNodesResult = node_0
@@ -290,31 +307,8 @@ async fn test_rpc_graph() {
290307

291308
#[tokio::test]
292309
async fn test_rpc_shutdown_channels() {
293-
let (nodes, _channels) = create_n_nodes_network_with_params(
294-
&[
295-
(
296-
(0, 1),
297-
ChannelParameters {
298-
public: true,
299-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
300-
node_b_funding_amount: MIN_RESERVED_CKB,
301-
..Default::default()
302-
},
303-
),
304-
(
305-
(0, 1),
306-
ChannelParameters {
307-
public: true,
308-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
309-
node_b_funding_amount: MIN_RESERVED_CKB,
310-
..Default::default()
311-
},
312-
),
313-
],
314-
2,
315-
Some(gen_rpc_config()),
316-
)
317-
.await;
310+
let (nodes, _channels) = gen_mock_network().await;
311+
318312
let [node_0, _node_1] = nodes.try_into().expect("2 nodes");
319313

320314
let list_channels: ListChannelsResult = node_0
@@ -413,31 +407,8 @@ async fn test_rpc_shutdown_channels() {
413407

414408
#[tokio::test]
415409
async fn test_rpc_node_info() {
416-
let (nodes, _channels) = create_n_nodes_network_with_params(
417-
&[
418-
(
419-
(0, 1),
420-
ChannelParameters {
421-
public: true,
422-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
423-
node_b_funding_amount: MIN_RESERVED_CKB,
424-
..Default::default()
425-
},
426-
),
427-
(
428-
(0, 1),
429-
ChannelParameters {
430-
public: true,
431-
node_a_funding_amount: MIN_RESERVED_CKB + 10000000000,
432-
node_b_funding_amount: MIN_RESERVED_CKB,
433-
..Default::default()
434-
},
435-
),
436-
],
437-
2,
438-
Some(gen_rpc_config()),
439-
)
440-
.await;
410+
let (nodes, _channels) = gen_mock_network().await;
411+
441412
let [node_0, _node_1] = nodes.try_into().expect("2 nodes");
442413

443414
let node_info: NodeInfoResult = node_0.send_rpc_request("node_info", ()).await.unwrap();
@@ -537,7 +508,8 @@ async fn test_rpc_basic_with_auth() {
537508
fallback_address: None,
538509
final_expiry_delta: Some(900000 + 1234),
539510
udt_type_script: Some(Script::default().into()),
540-
payment_preimage: Hash256::default(),
511+
payment_preimage: Some(Hash256::default()),
512+
payment_hash: None,
541513
hash_algorithm: Some(crate::fiber::hash_algorithm::HashAlgorithm::CkbHash),
542514
allow_mpp: None,
543515
atomic_mpp: None,

crates/fiber-lib/src/fiber/tests/types.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,13 +517,13 @@ fn test_verify_hard_coded_node_announcement() {
517517

518518
for (signature, message, node_announcement) in [
519519
(
520-
"80a0e9d4ed35eb76e086038983dfd2572e7298a795b4cde7b113d805eeb495192cf552e4cc631e73fee76c4f0479a33327488f8a99978a10e2583b7faba2cf61",
521-
"044e5172e9a9d7b383c15f40d8dac30f86422e7082af2b4d7db7f5484b4a1701",
520+
"cfe5d1dde285bac326fb290899c14bd05b9afa013c61c3c9338daca07ab0d287647c2101482bc193913b91da0e9284ebd352f1139149b8943876c78ff46608fc",
521+
"e387e01002f45594d36577ac53c822f8712b378ed52e8dde3bfbdb3b01b29abc",
522522
node1(),
523523
),
524524
(
525-
"3a1eea2e372e5c3bc53d1c283d449afbfff029308fe59e111a23ad32163d2a6f58128e21083e128a05d34fb8c0068a1f4fa6e4ae12e370d3051591df151a957a",
526-
"db96ac7278d1db7b03eefdc4d21c952e3aee8a4be87a82c8c0e66a87e8897a81",
525+
"1fec23d92c9fc9fafd39f477bf1fbb79cfb8f63604a6aeb0712cfd7dbe31e4e21a174f4e6733e78970f4489859aa1ba615fe712d4d212dd7f1c1a6678dff5d00",
526+
"3e612fcfa66885352ac18e1fdd602199fb125fa4435ea509f472c0c870b0d307",
527527
node2(),
528528
),
529529
] {

crates/fiber-lib/src/invoice/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub enum InvoiceError {
4747
DuplicatedAttributeKey(String),
4848
#[error("Payment secret is required for MPP payments")]
4949
PaymentSecretRequiredForMpp,
50+
/// Neither payment_hash nor payment_preimage is set
51+
#[error("Neither payment_hash nor payment_preimage is set")]
52+
NeitherPaymenthashNorPreimage,
5053
/// Both set payment_hash and payment_preimage
5154
#[error("Both payment_hash and payment_preimage are set")]
5255
BothPaymenthashAndPreimage,

crates/fiber-lib/src/invoice/invoice_impl.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::fiber::gen::invoice::{self as gen_invoice, *};
55
use crate::fiber::hash_algorithm::HashAlgorithm;
66
use crate::fiber::serde_utils::{duration_hex, EntityHex, U128Hex, U64Hex};
77
use crate::fiber::types::Hash256;
8-
use crate::gen_rand_sha256_hash;
98
use crate::invoice::InvoiceError;
109
use bech32::{encode, u5, FromBase32, ToBase32, Variant, WriteBase32};
1110
use bitcoin::hashes::{sha256::Hash as Sha256, Hash as _};
@@ -709,27 +708,22 @@ impl InvoiceBuilder {
709708
}
710709

711710
pub fn build(self) -> Result<CkbInvoice, InvoiceError> {
712-
let preimage = self.payment_preimage;
713-
714-
if self.payment_hash.is_some() && preimage.is_some() {
715-
return Err(InvoiceError::BothPaymenthashAndPreimage);
716-
}
717-
let payment_hash: Hash256 = if let Some(preimage) = preimage {
718-
let algo = self
719-
.attrs
720-
.iter()
721-
.find_map(|attr| match attr {
722-
Attribute::HashAlgorithm(algo) => Some(algo),
723-
_ => None,
724-
})
725-
.copied()
726-
.unwrap_or_default();
727-
algo.hash(preimage.as_ref()).into()
728-
} else if let Some(payment_hash) = self.payment_hash {
729-
payment_hash
730-
} else {
731-
// generate a random payment hash if not provided
732-
gen_rand_sha256_hash()
711+
let payment_hash: Hash256 = match (self.payment_hash, self.payment_preimage) {
712+
(Some(payment_hash), None) => payment_hash,
713+
(None, Some(preimage)) => {
714+
let algo = self
715+
.attrs
716+
.iter()
717+
.find_map(|attr| match attr {
718+
Attribute::HashAlgorithm(algo) => Some(algo),
719+
_ => None,
720+
})
721+
.copied()
722+
.unwrap_or_default();
723+
algo.hash(preimage.as_ref()).into()
724+
}
725+
(Some(_), Some(_)) => return Err(InvoiceError::BothPaymenthashAndPreimage),
726+
(None, None) => return Err(InvoiceError::NeitherPaymenthashNorPreimage),
733727
};
734728

735729
self.check_attrs_valid()?;

crates/fiber-lib/src/invoice/tests/invoice_impl.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,11 @@ fn test_invoice_rand_payment_hash() {
492492
let invoice = InvoiceBuilder::new(Currency::Fibb)
493493
.amount(Some(1280))
494494
.build_with_sign(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key));
495-
assert!(invoice.is_ok());
495+
eprintln!("invoice: {:?}", invoice);
496+
assert_eq!(
497+
invoice.err(),
498+
Some(InvoiceError::NeitherPaymenthashNorPreimage)
499+
);
496500
}
497501

498502
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]

crates/fiber-lib/src/rpc/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,14 +521,16 @@ Generates a new invoice.
521521
* `amount` - <em>`u128`</em>, The amount of the invoice.
522522
* `description` - <em>`Option<String>`</em>, The description of the invoice.
523523
* `currency` - <em>[Currency](#type-currency)</em>, The currency of the invoice.
524-
* `payment_preimage` - <em>[Hash256](#type-hash256)</em>, The payment preimage of the invoice.
524+
* `payment_preimage` - <em>Option<[Hash256](#type-hash256)></em>, The preimage to settle an incoming TLC payable to this invoice. If preimage is set, hash must be absent. If both preimage and hash are absent, a random preimage is generated.
525+
* `payment_hash` - <em>Option<[Hash256](#type-hash256)></em>, The hash of the preimage. If hash is set, preimage must be absent. This condition indicates a 'hold invoice' for which the tlc must be accepted and held until the preimage becomes known.
525526
* `expiry` - <em>`Option<u64>`</em>, The expiry time of the invoice, in seconds.
526527
* `fallback_address` - <em>`Option<String>`</em>, The fallback address of the invoice.
527528
* `final_expiry_delta` - <em>`Option<u64>`</em>, The final HTLC timeout of the invoice, in milliseconds.
528529
Minimal value is 16 hours, and maximal value is 14 days.
529530
* `udt_type_script` - <em>`Option<Script>`</em>, The UDT type script of the invoice.
530531
* `hash_algorithm` - <em>Option<[HashAlgorithm](#type-hashalgorithm)></em>, The hash algorithm of the invoice.
531532
* `allow_mpp` - <em>`Option<bool>`</em>, Whether allow payment to use MPP
533+
* `atomic_mpp` - <em>`Option<bool>`</em>, Whether use atomic mpp, if use atomic mpp there will be no preimage generated.
532534

533535
##### Returns
534536

crates/fiber-lib/src/rpc/invoice.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::invoice::{
1212
Currency, InvoiceBuilder, InvoiceData as InternalInvoiceData, InvoiceSignature, InvoiceStore,
1313
};
1414

15-
use crate::FiberConfig;
15+
use crate::{gen_rand_sha256_hash, FiberConfig};
1616
use ckb_jsonrpc_types::Script;
1717
use jsonrpsee::types::{error::CALL_EXECUTION_FAILED_CODE, ErrorObjectOwned};
1818

@@ -141,8 +141,10 @@ pub struct NewInvoiceParams {
141141
pub description: Option<String>,
142142
/// The currency of the invoice.
143143
pub currency: Currency,
144-
/// The payment preimage of the invoice.
145-
pub payment_preimage: Hash256,
144+
/// The preimage to settle an incoming TLC payable to this invoice. If preimage is set, hash must be absent. If both preimage and hash are absent, a random preimage is generated.
145+
pub payment_preimage: Option<Hash256>,
146+
/// The hash of the preimage. If hash is set, preimage must be absent. This condition indicates a 'hold invoice' for which the tlc must be accepted and held until the preimage becomes known.
147+
pub payment_hash: Option<Hash256>,
146148
/// The expiry time of the invoice, in seconds.
147149
#[serde_as(as = "Option<U64Hex>")]
148150
pub expiry: Option<u64>,
@@ -325,9 +327,23 @@ where
325327
));
326328
}
327329
}
328-
let mut invoice_builder = InvoiceBuilder::new(params.currency)
329-
.amount(Some(params.amount))
330-
.payment_preimage(params.payment_preimage);
330+
331+
let mut invoice_builder = InvoiceBuilder::new(params.currency).amount(Some(params.amount));
332+
333+
// If both preimage and payment hash are absent, a random preimage is generated.
334+
let need_gen_preimage =
335+
params.payment_hash.is_none() && params.atomic_mpp.is_none_or(|atomic| !atomic);
336+
let preimage_opt = params
337+
.payment_preimage
338+
.or_else(|| need_gen_preimage.then(gen_rand_sha256_hash));
339+
340+
if let Some(preimage) = preimage_opt {
341+
invoice_builder = invoice_builder.payment_preimage(preimage);
342+
}
343+
if let Some(hash) = params.payment_hash {
344+
invoice_builder = invoice_builder.payment_hash(hash);
345+
}
346+
331347
if let Some(description) = params.description.clone() {
332348
invoice_builder = invoice_builder.description(description);
333349
};
@@ -391,10 +407,7 @@ where
391407
};
392408

393409
match invoice {
394-
Ok(invoice) => match self
395-
.store
396-
.insert_invoice(invoice.clone(), Some(params.payment_preimage))
397-
{
410+
Ok(invoice) => match self.store.insert_invoice(invoice.clone(), preimage_opt) {
398411
Ok(_) => Ok(InvoiceResult {
399412
invoice_address: invoice.to_string(),
400413
invoice: invoice.into(),

crates/fiber-lib/src/tests/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ impl NetworkNode {
908908
params: P,
909909
) -> Result<R, String> {
910910
let response = self.send_rpc_request_raw(method, params).await?;
911+
eprintln!("response: {:?}", response);
911912
let result = serde_json::from_value::<R>(response.clone())
912913
.map_err(|e| format!("failed to deserialize response: {}", e))?;
913914
Ok(result)

0 commit comments

Comments
 (0)