@@ -13,7 +13,7 @@ use crate::ln::msgs;
13
13
use crate :: ln:: wire:: Encode ;
14
14
use crate :: routing:: gossip:: NetworkUpdate ;
15
15
use crate :: routing:: router:: { BlindedTail , Path , RouteHop } ;
16
- use crate :: sign:: NodeSigner ;
16
+ use crate :: sign:: { NodeSigner , Recipient } ;
17
17
use crate :: util:: chacha20:: { ChaCha20 , ChaChaReader } ;
18
18
use crate :: util:: errors:: { self , APIError } ;
19
19
use crate :: util:: ser:: { Readable , ReadableArgs , Writeable , Writer , LengthCalculatingWriter } ;
@@ -935,6 +935,180 @@ pub(crate) fn decode_next_payment_hop<NS: Deref>(
935
935
}
936
936
}
937
937
938
+ /// Build a payment onion, returning the first hop msat and cltv values as well.
939
+ pub fn create_payment_onion < T > (
940
+ secp_ctx : & Secp256k1 < T > , path : & Path , session_priv : & SecretKey , total_msat : u64 ,
941
+ recipient_onion : RecipientOnionFields , best_block_height : u32 , payment_hash : PaymentHash ,
942
+ keysend_preimage : Option < PaymentPreimage > , prng_seed : [ u8 ; 32 ]
943
+ ) -> Result < ( u64 , u32 , msgs:: OnionPacket ) , ( ) >
944
+ where
945
+ T : secp256k1:: Signing
946
+ {
947
+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| ( ) ) ?;
948
+ let ( onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
949
+ & path,
950
+ total_msat,
951
+ recipient_onion,
952
+ best_block_height + 1 ,
953
+ & keysend_preimage,
954
+ ) . map_err ( |_| ( ) ) ?;
955
+ let onion_packet = construct_onion_packet (
956
+ onion_payloads, onion_keys, prng_seed, & payment_hash
957
+ ) ?;
958
+ Ok ( ( htlc_msat, htlc_cltv, onion_packet) )
959
+ }
960
+
961
+ /// Forwarded Payment, including next hop details.
962
+ pub struct ForwardedPayment {
963
+ /// Short channel id of the next hop.
964
+ pub short_channel_id : u64 ,
965
+ /// The value, in msat, of the payment after this hop's fee is deducted.
966
+ pub amt_to_forward : u64 ,
967
+ /// Outgoing CLTV for the next hop.
968
+ pub outgoing_cltv_value : u32 ,
969
+ /// Onion packet for the next hop.
970
+ pub onion_packet : msgs:: OnionPacket ,
971
+ }
972
+
973
+ /// Received payment, of either regular or blinded type.
974
+ pub enum ReceivedPayment {
975
+ /// Regular (unblinded) payment.
976
+ Unblinded {
977
+ /// Payment_secret to authenticate sender to the receiver.
978
+ payment_secret : Option < [ u8 ; 32 ] > ,
979
+ /// The total value, in msat, of the payment as received by the ultimate recipient.
980
+ total_msat : Option < u64 > ,
981
+ /// Custom payment metadata included in the payment.
982
+ payment_metadata : Option < Vec < u8 > > ,
983
+ /// Preimage used in spontaneous payment.
984
+ keysend_preimage : Option < [ u8 ; 32 ] > ,
985
+ /// Custom TLV records included in the payment.
986
+ custom_tlvs : Vec < ( u64 , Vec < u8 > ) > ,
987
+ /// Amount received.
988
+ amt_msat : u64 ,
989
+ /// CLTV expiration.
990
+ cltv_expiry : u32 ,
991
+ } ,
992
+ /// Blinded payment
993
+ Blinded {
994
+ /// Amount received.
995
+ amt_msat : u64 ,
996
+ /// Amount received plus fees paid.
997
+ total_msat : u64 ,
998
+ /// CLTV expiration.
999
+ cltv_expiry : u32 ,
1000
+ /// Payment secret.
1001
+ payment_secret : [ u8 ; 32 ] ,
1002
+ /// The maximum total CLTV that is acceptable when relaying a payment over this hop.
1003
+ max_cltv_expiry : u32 ,
1004
+ /// The minimum value, in msat, that may be accepted by the node corresponding to this hop.
1005
+ htlc_minimum_msat : u64 ,
1006
+ /// Blinding point from intro node.
1007
+ intro_node_blinding_point : PublicKey ,
1008
+ }
1009
+ }
1010
+
1011
+ impl core:: convert:: TryFrom < msgs:: InboundOnionPayload > for ReceivedPayment {
1012
+ type Error = ( ) ;
1013
+ fn try_from ( pld : msgs:: InboundOnionPayload ) -> Result < Self , Self :: Error > {
1014
+ match pld {
1015
+ msgs:: InboundOnionPayload :: Forward {
1016
+ short_channel_id : _, amt_to_forward : _, outgoing_cltv_value : _
1017
+ } => {
1018
+ Err ( ( ) )
1019
+ } ,
1020
+ msgs:: InboundOnionPayload :: Receive {
1021
+ payment_data, payment_metadata, keysend_preimage, custom_tlvs, amt_msat,
1022
+ outgoing_cltv_value
1023
+ } => {
1024
+ let ( payment_secret, total_msat) = match payment_data {
1025
+ Some ( p) => ( Some ( p. payment_secret . 0 ) , Some ( p. total_msat ) ) ,
1026
+ None => ( None , None ) ,
1027
+ } ;
1028
+ let keysend_preimage = keysend_preimage. map ( |p| p. 0 ) ;
1029
+ Ok ( Self :: Unblinded {
1030
+ payment_secret, total_msat, payment_metadata, keysend_preimage, custom_tlvs,
1031
+ amt_msat, cltv_expiry : outgoing_cltv_value
1032
+ } )
1033
+ } ,
1034
+ msgs:: InboundOnionPayload :: BlindedReceive {
1035
+ amt_msat, total_msat, outgoing_cltv_value, payment_secret, payment_constraints,
1036
+ intro_node_blinding_point
1037
+ } => {
1038
+ let payment_secret = payment_secret. 0 ;
1039
+ let max_cltv_expiry = payment_constraints. max_cltv_expiry ;
1040
+ let htlc_minimum_msat = payment_constraints. htlc_minimum_msat ;
1041
+ Ok ( Self :: Blinded {
1042
+ amt_msat, total_msat, cltv_expiry : outgoing_cltv_value, payment_secret,
1043
+ max_cltv_expiry, htlc_minimum_msat, intro_node_blinding_point
1044
+ } )
1045
+ }
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ /// Received and decrypted onion payment, either of type Receive (for us), or Forward.
1051
+ pub enum PeeledPayment {
1052
+ /// This onion payload was for us, not for forwarding to a next-hop.
1053
+ Receive ( ReceivedPayment ) ,
1054
+ /// This onion payload to be forwarded to next peer.
1055
+ Forward ( ForwardedPayment ) ,
1056
+ }
1057
+
1058
+ /// Unwrap one layer of an incoming HTLC, returning either or a received payment, or a another
1059
+ /// onion to forward.
1060
+ pub fn peel_payment_onion < NS : Deref > (
1061
+ onion : & msgs:: OnionPacket , payment_hash : PaymentHash , node_signer : & NS ,
1062
+ secp_ctx : & Secp256k1 < secp256k1:: All >
1063
+ ) -> Result < PeeledPayment , ( ) >
1064
+ where
1065
+ NS :: Target : NodeSigner ,
1066
+ {
1067
+ if onion. public_key . is_err ( ) {
1068
+ return Err ( ( ) ) ;
1069
+ }
1070
+ let shared_secret = node_signer
1071
+ . ecdh ( Recipient :: Node , & onion. public_key . unwrap ( ) , None )
1072
+ . unwrap ( )
1073
+ . secret_bytes ( ) ;
1074
+
1075
+ let hop = decode_next_payment_hop (
1076
+ shared_secret, & onion. hop_data [ ..] , onion. hmac , payment_hash, node_signer
1077
+ ) . map_err ( |_| ( ) ) ?;
1078
+ let peeled = match hop {
1079
+ Hop :: Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
1080
+ if let msgs:: InboundOnionPayload :: Forward {
1081
+ short_channel_id, amt_to_forward, outgoing_cltv_value
1082
+ } = next_hop_data {
1083
+
1084
+ let next_packet_pk = next_hop_pubkey (
1085
+ secp_ctx, onion. public_key . unwrap ( ) , & shared_secret
1086
+ ) ;
1087
+
1088
+ let onion_packet = msgs:: OnionPacket {
1089
+ version : 0 ,
1090
+ public_key : next_packet_pk,
1091
+ hop_data : new_packet_bytes,
1092
+ hmac : next_hop_hmac,
1093
+ } ;
1094
+ PeeledPayment :: Forward ( ForwardedPayment {
1095
+ short_channel_id,
1096
+ amt_to_forward,
1097
+ outgoing_cltv_value,
1098
+ onion_packet,
1099
+ } )
1100
+ } else {
1101
+ return Err ( ( ) ) ;
1102
+ }
1103
+ } ,
1104
+ Hop :: Receive ( inbound) => {
1105
+ let payload: ReceivedPayment = inbound. try_into ( ) . map_err ( |_| ( ) ) ?;
1106
+ PeeledPayment :: Receive ( payload)
1107
+ } ,
1108
+ } ;
1109
+ Ok ( peeled)
1110
+ }
1111
+
938
1112
pub ( crate ) fn decode_next_untagged_hop < T , R : ReadableArgs < T > , N : NextPacketBytes > ( shared_secret : [ u8 ; 32 ] , hop_data : & [ u8 ] , hmac_bytes : [ u8 ; 32 ] , read_args : T ) -> Result < ( R , Option < ( [ u8 ; 32 ] , N ) > ) , OnionDecodeErr > {
939
1113
decode_next_hop ( shared_secret, hop_data, hmac_bytes, None , read_args)
940
1114
}
@@ -1019,7 +1193,8 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8
1019
1193
#[ cfg( test) ]
1020
1194
mod tests {
1021
1195
use crate :: io;
1022
- use crate :: prelude:: * ;
1196
+ use crate :: ln:: { PaymentPreimage , PaymentSecret } ;
1197
+ use crate :: prelude:: * ;
1023
1198
use crate :: ln:: PaymentHash ;
1024
1199
use crate :: ln:: features:: { ChannelFeatures , NodeFeatures } ;
1025
1200
use crate :: routing:: router:: { Path , Route , RouteHop } ;
@@ -1235,4 +1410,117 @@ mod tests {
1235
1410
writer. write_all ( & self . data [ ..] )
1236
1411
}
1237
1412
}
1413
+
1414
+ use crate :: ln:: channelmanager:: RecipientOnionFields ;
1415
+ use crate :: sign:: KeysManager ;
1416
+ use super :: { create_payment_onion, peel_payment_onion} ;
1417
+ use super :: { PeeledPayment , ReceivedPayment } ;
1418
+
1419
+ fn payment_onion_args ( hop_pk : PublicKey , recipient_pk : PublicKey ) -> (
1420
+ SecretKey , u64 , u32 , RecipientOnionFields , PaymentPreimage , PaymentHash , [ u8 ; 32 ] ,
1421
+ Vec < RouteHop > , u64 , PaymentSecret ,
1422
+ ) {
1423
+ use core:: convert:: TryInto ;
1424
+ use super :: { Sha256 , Hash } ;
1425
+
1426
+ let session_priv_bytes = [ 42 ; 32 ] ;
1427
+ let session_priv = SecretKey :: from_slice ( & session_priv_bytes) . unwrap ( ) ;
1428
+ let total_amt_msat = 1000 ;
1429
+ let cur_height = 1000 ;
1430
+ let pay_secret = PaymentSecret ( [ 99 ; 32 ] ) ;
1431
+ let recipient_onion = RecipientOnionFields :: secret_only ( pay_secret) ;
1432
+ let preimage_bytes = [ 43 ; 32 ] ;
1433
+ let preimage = PaymentPreimage ( preimage_bytes) ;
1434
+ let rhash = Sha256 :: hash ( & preimage_bytes) . to_vec ( ) ;
1435
+ let rhash_bytes: [ u8 ; 32 ] = rhash. try_into ( ) . unwrap ( ) ;
1436
+ let payment_hash = PaymentHash ( rhash_bytes) ;
1437
+ let prng_seed = [ 44 ; 32 ] ;
1438
+
1439
+ // make a route alice -> bob -> charlie
1440
+ let hop_fee = 1 ;
1441
+ let recipient_amount = total_amt_msat - hop_fee;
1442
+ let hops = vec ! [
1443
+ RouteHop {
1444
+ pubkey: hop_pk,
1445
+ fee_msat: hop_fee,
1446
+ cltv_expiry_delta: 0 ,
1447
+ short_channel_id: 1 ,
1448
+ node_features: NodeFeatures :: empty( ) ,
1449
+ channel_features: ChannelFeatures :: empty( ) ,
1450
+ maybe_announced_channel: false ,
1451
+ } ,
1452
+ RouteHop {
1453
+ pubkey: recipient_pk,
1454
+ fee_msat: recipient_amount,
1455
+ cltv_expiry_delta: 0 ,
1456
+ short_channel_id: 2 ,
1457
+ node_features: NodeFeatures :: empty( ) ,
1458
+ channel_features: ChannelFeatures :: empty( ) ,
1459
+ maybe_announced_channel: false ,
1460
+ }
1461
+ ] ;
1462
+
1463
+ ( session_priv, total_amt_msat, cur_height, recipient_onion, preimage, payment_hash,
1464
+ prng_seed, hops, recipient_amount, pay_secret)
1465
+ }
1466
+
1467
+ #[ test]
1468
+ fn create_and_peel_unblinded_payment ( ) {
1469
+ let secp_ctx = Secp256k1 :: new ( ) ;
1470
+
1471
+ // let alice = make_keys_manager(&[1; 32]);
1472
+ let bob = make_keys_manager ( & [ 2 ; 32 ] ) ;
1473
+ let bob_pk = PublicKey :: from_secret_key ( & secp_ctx, & bob. get_node_secret_key ( ) ) ;
1474
+ let charlie = make_keys_manager ( & [ 3 ; 32 ] ) ;
1475
+ let charlie_pk = PublicKey :: from_secret_key ( & secp_ctx, & charlie. get_node_secret_key ( ) ) ;
1476
+
1477
+ let ( session_priv, total_amt_msat, cur_height, recipient_onion, preimage, payment_hash,
1478
+ prng_seed, hops, recipient_amount, pay_secret) = payment_onion_args ( bob_pk, charlie_pk) ;
1479
+
1480
+ let path = Path {
1481
+ hops : hops,
1482
+ blinded_tail : None ,
1483
+ } ;
1484
+
1485
+ let ( _htlc_msat, _htlc_cltv, onion) = create_payment_onion (
1486
+ & secp_ctx, & path, & session_priv, total_amt_msat, recipient_onion, cur_height,
1487
+ payment_hash, Some ( preimage) , prng_seed
1488
+ ) . unwrap ( ) ;
1489
+
1490
+ // bob peels to find another onion
1491
+ let next_onion = match peel_payment_onion ( & onion, payment_hash, & & bob, & secp_ctx) . unwrap ( ) {
1492
+ PeeledPayment :: Receive ( _) => panic ! ( "should not be Receive" ) ,
1493
+ PeeledPayment :: Forward ( forwarded) => forwarded. onion_packet ,
1494
+ } ;
1495
+
1496
+ // charlie peels to find a received payment
1497
+ let received = match peel_payment_onion (
1498
+ & next_onion, payment_hash, & & charlie, & secp_ctx
1499
+ ) . unwrap ( ) {
1500
+ PeeledPayment :: Receive ( received) => received,
1501
+ PeeledPayment :: Forward ( _) => panic ! ( "should not be a Forward" ) ,
1502
+ } ;
1503
+
1504
+ match received {
1505
+ ReceivedPayment :: Unblinded {
1506
+ amt_msat, keysend_preimage, payment_secret, total_msat, payment_metadata,
1507
+ custom_tlvs, cltv_expiry
1508
+ } => {
1509
+ assert_eq ! ( amt_msat, recipient_amount) ;
1510
+ assert_eq ! ( keysend_preimage, Some ( preimage. 0 ) ) ;
1511
+ assert_eq ! ( payment_secret, Some ( pay_secret. 0 ) ) ;
1512
+ assert_eq ! ( total_msat, Some ( total_amt_msat) ) ;
1513
+ assert_eq ! ( payment_metadata, None ) ;
1514
+ assert_eq ! ( custom_tlvs, Vec :: new( ) ) ;
1515
+ assert_eq ! ( cltv_expiry, cur_height + 1 ) ;
1516
+ } ,
1517
+ ReceivedPayment :: Blinded { .. } => panic ! ( "Should not be Blinded" ) ,
1518
+ }
1519
+
1520
+ }
1521
+
1522
+ fn make_keys_manager ( seed : & [ u8 ; 32 ] ) -> KeysManager {
1523
+ KeysManager :: new ( seed, 42 , 42 )
1524
+ }
1525
+
1238
1526
}
0 commit comments