bolt12: use spec field names, update decode API.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2022-11-09 13:02:00 +10:30
committed by Christian Decker
parent fdb3d9186a
commit ef2f4a0648
18 changed files with 1466 additions and 912 deletions

View File

@@ -272,6 +272,7 @@
"CreateInvoice.bolt12": 3,
"CreateInvoice.description": 7,
"CreateInvoice.expires_at": 8,
"CreateInvoice.invreq_payer_note": 15,
"CreateInvoice.label": 1,
"CreateInvoice.local_offer_id": 13,
"CreateInvoice.paid_at": 11,
@@ -336,6 +337,7 @@
"DelInvoice.bolt12": 3,
"DelInvoice.description": 5,
"DelInvoice.expires_at": 8,
"DelInvoice.invreq_payer_note": 11,
"DelInvoice.label": 1,
"DelInvoice.local_offer_id": 9,
"DelInvoice.payer_note": 10,
@@ -630,6 +632,7 @@
"ListInvoices.invoices[].bolt12": 8,
"ListInvoices.invoices[].description": 2,
"ListInvoices.invoices[].expires_at": 5,
"ListInvoices.invoices[].invreq_payer_note": 15,
"ListInvoices.invoices[].label": 1,
"ListInvoices.invoices[].local_offer_id": 9,
"ListInvoices.invoices[].paid_at": 13,

View File

@@ -481,7 +481,7 @@ message CreateinvoiceResponse {
optional uint64 paid_at = 11;
optional bytes payment_preimage = 12;
optional bytes local_offer_id = 13;
optional string payer_note = 14;
optional string invreq_payer_note = 15;
}
message DatastoreRequest {
@@ -571,7 +571,7 @@ message DelinvoiceResponse {
DelinvoiceStatus status = 7;
uint64 expires_at = 8;
optional bytes local_offer_id = 9;
optional string payer_note = 10;
optional string invreq_payer_note = 11;
}
message InvoiceRequest {
@@ -640,7 +640,7 @@ message ListinvoicesInvoices {
optional string bolt11 = 7;
optional string bolt12 = 8;
optional bytes local_offer_id = 9;
optional string payer_note = 10;
optional string invreq_payer_note = 15;
optional uint64 pay_index = 11;
optional Amount amount_received_msat = 12;
optional uint64 paid_at = 13;

View File

@@ -348,7 +348,7 @@ impl From<responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse {
paid_at: c.paid_at, // Rule #2 for type u64?
payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret?
local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex?
payer_note: c.payer_note, // Rule #2 for type string?
invreq_payer_note: c.invreq_payer_note, // Rule #2 for type string?
}
}
}
@@ -408,7 +408,7 @@ impl From<responses::DelinvoiceResponse> for pb::DelinvoiceResponse {
status: c.status as i32,
expires_at: c.expires_at, // Rule #2 for type u64
local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex?
payer_note: c.payer_note, // Rule #2 for type string?
invreq_payer_note: c.invreq_payer_note, // Rule #2 for type string?
}
}
}
@@ -464,7 +464,7 @@ impl From<responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices {
bolt11: c.bolt11, // Rule #2 for type string?
bolt12: c.bolt12, // Rule #2 for type string?
local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex?
payer_note: c.payer_note, // Rule #2 for type string?
invreq_payer_note: c.invreq_payer_note, // Rule #2 for type string?
pay_index: c.pay_index, // Rule #2 for type u64?
amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat?
paid_at: c.paid_at, // Rule #2 for type u64?

12
cln-rpc/src/model.rs generated
View File

@@ -2258,8 +2258,8 @@ pub mod responses {
pub payment_preimage: Option<Secret>,
#[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")]
pub local_offer_id: Option<String>,
#[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")]
pub payer_note: Option<String>,
#[serde(alias = "invreq_payer_note", skip_serializing_if = "Option::is_none")]
pub invreq_payer_note: Option<String>,
}
impl TryFrom<Response> for CreateinvoiceResponse {
@@ -2396,8 +2396,8 @@ pub mod responses {
pub expires_at: u64,
#[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")]
pub local_offer_id: Option<String>,
#[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")]
pub payer_note: Option<String>,
#[serde(alias = "invreq_payer_note", skip_serializing_if = "Option::is_none")]
pub invreq_payer_note: Option<String>,
}
impl TryFrom<Response> for DelinvoiceResponse {
@@ -2516,8 +2516,8 @@ pub mod responses {
pub bolt12: Option<String>,
#[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")]
pub local_offer_id: Option<String>,
#[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")]
pub payer_note: Option<String>,
#[serde(alias = "invreq_payer_note", skip_serializing_if = "Option::is_none")]
pub invreq_payer_note: Option<String>,
#[serde(alias = "pay_index", skip_serializing_if = "Option::is_none")]
pub pay_index: Option<u64>,
#[serde(alias = "amount_received_msat", skip_serializing_if = "Option::is_none")]

View File

@@ -339,7 +339,7 @@ def createinvoice2py(m):
"paid_at": m.paid_at, # PrimitiveField in generate_composite
"payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite
"local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite
"payer_note": m.payer_note, # PrimitiveField in generate_composite
"invreq_payer_note": m.invreq_payer_note, # PrimitiveField in generate_composite
})
@@ -384,7 +384,7 @@ def delinvoice2py(m):
"status": str(m.status), # EnumField in generate_composite
"expires_at": m.expires_at, # PrimitiveField in generate_composite
"local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite
"payer_note": m.payer_note, # PrimitiveField in generate_composite
"invreq_payer_note": m.invreq_payer_note, # PrimitiveField in generate_composite
})
@@ -428,7 +428,7 @@ def listinvoices_invoices2py(m):
"bolt11": m.bolt11, # PrimitiveField in generate_composite
"bolt12": m.bolt12, # PrimitiveField in generate_composite
"local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite
"payer_note": m.payer_note, # PrimitiveField in generate_composite
"invreq_payer_note": m.invreq_payer_note, # PrimitiveField in generate_composite
"pay_index": m.pay_index, # PrimitiveField in generate_composite
"amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite
"paid_at": m.paid_at, # PrimitiveField in generate_composite

File diff suppressed because one or more lines are too long

View File

@@ -46,7 +46,7 @@ On success, an object is returned, containing:
- **paid\_at** (u64, optional): UNIX timestamp of when invoice was paid (**status** *paid* only)
- **payment\_preimage** (secret, optional): the proof of payment: SHA256 of this **payment_hash** (always 64 characters)
- **local\_offer\_id** (hex, optional): the *id* of our offer which created this invoice (**experimental-offers** only). (always 64 characters)
- **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only).
- **invreq\_payer\_note** (string, optional): the optional *invreq_payer_note* from invoice_request which created this invoice (**experimental-offers** only).
[comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -75,4 +75,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:9fc2c7cb6e5980774a768dd9ddfe81e254c084554c159e6b07e92e703dc10595)
[comment]: # ( SHA256STAMP:3acf2924a8670605f70a7976cf4909b60addf4b1aeebc9b9a104151cffa2c984)

View File

@@ -29,127 +29,202 @@ On success, an object is returned, containing:
If **type** is "bolt12 offer", and **valid** is *true*:
- **offer\_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters)
- **node\_id** (pubkey): public key of the offering node
- **description** (string): the description of the purpose of the offer
- **signature** (bip340sig, optional): BIP-340 signature of the *node_id* on this offer
- **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
- **offer\_id** (hex): the id we use to identify this offer (always 64 characters)
- **offer\_description** (string): the description of the purpose of the offer
- **offer\_node\_id** (pubkey): public key of the offering node
- **offer\_chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
- the genesis blockhash (always 64 characters)
- **currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters)
- **minor\_unit** (u32, optional): the number of decimal places to apply to amount (if currency known)
- **amount** (u64, optional): the amount in the *currency* adjusted by *minor_unit*, if any
- **amount\_msat** (msat, optional): the amount in bitcoin (if specified, and no *currency*)
- **send\_invoice** (boolean, optional): present if this is a send_invoice offer (always *true*)
- **refund\_for** (hex, optional): the *payment_preimage* of invoice this is a refund for (always 64 characters)
- **vendor** (string, optional): the name of the vendor for this offer
- **features** (hex, optional): the array of feature bits for this offer
- **absolute\_expiry** (u64, optional): UNIX timestamp of when this offer expires
- **paths** (array of objects, optional): Paths to the destination:
- **offer\_metadata** (hex, optional): any metadata the creater of the offer includes
- **offer\_currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters)
- **currency\_minor\_unit** (u32, optional): the number of decimal places to apply to amount (if currency known)
- **offer\_amount** (u64, optional): the amount in the `offer_currency` adjusted by `currency_minor_unit`, if any
- **offer\_amount\_msat** (msat, optional): the amount in bitcoin (if specified, and no `offer_currency`)
- **offer\_issuer** (string, optional): the description of the creator of the offer
- **offer\_features** (hex, optional): the feature bits of the offer
- **offer\_absolute\_expiry** (u64, optional): UNIX timestamp of when this offer expires
- **offer\_quantity\_max** (u64, optional): the maximum quantity (or, if 0, means any quantity)
- **offer\_paths** (array of objects, optional): Paths to the destination:
- **first\_node\_id** (pubkey): the (presumably well-known) public key of the start of the path
- **blinding** (pubkey): blinding factor for this path
- **path** (array of objects): an individual path:
- **blinded\_node\_id** (pubkey): node_id of the hop
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this hop
- **quantity\_max** (u64, optional): the maximum quantity (or, if 0, means any quantity)
- **recurrence** (object, optional): how often to this offer should be used:
- **offer\_recurrence** (object, optional): how often to this offer should be used:
- **time\_unit** (u32): the BOLT12 time unit
- **period** (u32): how many *time_unit* per payment period
- **time\_unit\_name** (string, optional): the name of *time_unit* (if valid)
- **period** (u32): how many `time_unit` per payment period
- **time\_unit\_name** (string, optional): the name of `time_unit` (if valid)
- **basetime** (u64, optional): period starts at this UNIX timestamp
- **start\_any\_period** (u64, optional): you can start at any period (only if **basetime** present)
- **start\_any\_period** (u64, optional): you can start at any period (only if `basetime` present)
- **limit** (u32, optional): maximum period number for recurrence
- **paywindow** (object, optional): when within a period will payment be accepted (default is prior and during the period):
- **seconds\_before** (u32): seconds prior to period start
- **seconds\_after** (u32): seconds after to period start
- **proportional\_amount** (boolean, optional): amount should be scaled if payed after period start (always *true*)
- the following warnings are possible:
- **warning\_offer\_unknown\_currency**: The currency code is unknown (so no **minor_unit**)
- **warning\_unknown\_offer\_currency**: The currency code is unknown (so no `currency_minor_unit`)
If **type** is "bolt12 offer", and **valid** is *false*:
- the following warnings are possible:
- **warning\_offer\_missing\_description**: No **description**
- **warning\_missing\_offer\_node\_id**: `offer_node_id` is not present
- **warning\_invalid\_offer\_description**: `offer_description` is not valid UTF8
- **warning\_missing\_offer\_description**: `offer_description` is not present
- **warning\_invalid\_offer\_currency**: `offer_currency_code` is not valid UTF8
- **warning\_invalid\_offer\_issuer**: `offer_issuer` is not valid UTF8
If **type** is "bolt12 invoice", and **valid** is *true*:
If **type** is "bolt12 invoice_request", and **valid** is *true*:
- **node\_id** (pubkey): public key of the offering node
- **signature** (bip340sig): BIP-340 signature of the *node_id* on this invoice
- **amount\_msat** (msat): the amount in bitcoin
- **description** (string): the description of the purpose of the offer
- **created\_at** (u64): the UNIX timestamp of invoice creation
- **payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters)
- **relative\_expiry** (u32): the number of seconds after *created_at* when this expires
- **offer\_id** (hex, optional): the id of this offer (merkle hash of non-signature fields) (always 64 characters)
- **chain** (hex, optional): which blockchain this invoice is for (missing implies bitcoin mainnet only) (always 64 characters)
- **send\_invoice** (boolean, optional): present if this offer was a send_invoice offer (always *true*)
- **refund\_for** (hex, optional): the *payment_preimage* of invoice this is a refund for (always 64 characters)
- **vendor** (string, optional): the name of the vendor for this offer
- **features** (hex, optional): the array of feature bits for this offer
- **paths** (array of objects, optional): Paths to the destination:
- **offer\_description** (string): the description of the purpose of the offer
- **offer\_node\_id** (pubkey): public key of the offering node
- **invreq\_metadata** (hex): the payer-provided blob to derive invreq_payer_id
- **invreq\_payer\_id** (hex): the payer-provided key
- **signature** (bip340sig): BIP-340 signature of the `invreq_payer_id` on this invoice_request
- **offer\_id** (hex, optional): the id we use to identify this offer (always 64 characters)
- **offer\_chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
- the genesis blockhash (always 64 characters)
- **offer\_metadata** (hex, optional): any metadata the creator of the offer includes
- **offer\_currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters)
- **currency\_minor\_unit** (u32, optional): the number of decimal places to apply to amount (if currency known)
- **offer\_amount** (u64, optional): the amount in the `offer_currency` adjusted by `currency_minor_unit`, if any
- **offer\_amount\_msat** (msat, optional): the amount in bitcoin (if specified, and no `offer_currency`)
- **offer\_issuer** (string, optional): the description of the creator of the offer
- **offer\_features** (hex, optional): the feature bits of the offer
- **offer\_absolute\_expiry** (u64, optional): UNIX timestamp of when this offer expires
- **offer\_quantity\_max** (u64, optional): the maximum quantity (or, if 0, means any quantity)
- **offer\_paths** (array of objects, optional): Paths to the destination:
- **first\_node\_id** (pubkey): the (presumably well-known) public key of the start of the path
- **blinding** (pubkey): blinding factor for this path
- **path** (array of objects): an individual path:
- **blinded\_node\_id** (pubkey): node_id of the hop
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this hop
- **fee\_base\_msat** (msat, optional): base fee for the entire path
- **fee\_proportional\_millionths** (u32, optional): proportional fee for the entire path
- **cltv\_expiry\_delta** (u32, optional): total CLTV delta across path
- **features** (hex, optional): Features allowed/required for this path
- **quantity** (u64, optional): the quantity ordered
- **recurrence\_counter** (u32, optional): the 0-based counter for a recurring payment
- **recurrence\_start** (u32, optional): the optional start period for a recurring payment
- **recurrence\_basetime** (u32, optional): the UNIX timestamp of the first recurrence period start
- **payer\_key** (pubkey, optional): the transient key which identifies the payer
- **invreq\_metadata** (hex, optional): the payer-provided blob to derive payer_key
- **fallbacks** (array of objects, optional): onchain addresses:
- **offer\_recurrence** (object, optional): how often to this offer should be used:
- **time\_unit** (u32): the BOLT12 time unit
- **period** (u32): how many `time_unit` per payment period
- **time\_unit\_name** (string, optional): the name of `time_unit` (if valid)
- **basetime** (u64, optional): period starts at this UNIX timestamp
- **start\_any\_period** (u64, optional): you can start at any period (only if `basetime` present)
- **limit** (u32, optional): maximum period number for recurrence
- **paywindow** (object, optional): when within a period will payment be accepted (default is prior and during the period):
- **seconds\_before** (u32): seconds prior to period start
- **seconds\_after** (u32): seconds after to period start
- **proportional\_amount** (boolean, optional): amount should be scaled if payed after period start (always *true*)
- **invreq\_chain** (hex, optional): which blockchain this offer is for (missing implies bitcoin mainnet only) (always 64 characters)
- **invreq\_amount\_msat** (msat, optional): the amount the invoice should be for
- **invreq\_features** (hex, optional): the feature bits of the invoice_request
- **invreq\_quantity** (u64, optional): the number of items to invoice for
- **invreq\_payer\_note** (string, optional): a note attached by the payer
- **invreq\_recurrence\_counter** (u32, optional): which number request this is for the same invoice
- **invreq\_recurrence\_start** (u32, optional): when we're requesting to start an invoice at a non-zero period
- the following warnings are possible:
- **warning\_unknown\_offer\_currency**: The currency code is unknown (so no `currency_minor_unit`)
If **type** is "bolt12 invoice_request", and **valid** is *false*:
- the following warnings are possible:
- **warning\_invalid\_offer\_description**: `offer_description` is not valid UTF8
- **warning\_missing\_offer\_description**: `offer_description` is not present
- **warning\_invalid\_offer\_currency**: `offer_currency_code` is not valid UTF8
- **warning\_invalid\_offer\_issuer**: `offer_issuer` is not valid UTF8
- **warning\_missing\_invreq\_metadata**: `invreq_metadata` is not present
- **warning\_missing\_invreq\_payer\_id**: `invreq_payer_id` is not present
- **warning\_invalid\_invreq\_payer\_note**: `invreq_payer_note` is not valid UTF8
- **warning\_missing\_invoice\_request\_signature**: `signature` is not present
- **warning\_invalid\_invoice\_request\_signature**: Incorrect `signature`
If **type** is "bolt12 invoice", and **valid** is *true*:
- **offer\_description** (string): the description of the purpose of the offer
- **offer\_node\_id** (pubkey): public key of the offering node
- **invreq\_metadata** (hex): the payer-provided blob to derive invreq_payer_id
- **invreq\_payer\_id** (hex): the payer-provided key
- **invoice\_paths** (array of objects): Paths to pay the destination:
- **first\_node\_id** (pubkey): the (presumably well-known) public key of the start of the path
- **blinding** (pubkey): blinding factor for this path
- **path** (array of objects): an individual path:
- **blinded\_node\_id** (pubkey): node_id of the hop
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this hop
- **fee\_base\_msat** (msat, optional): basefee for path
- **fee\_proportional\_millionths** (u32, optional): proportional fee for path
- **cltv\_expiry\_delta** (u32, optional): CLTV delta for path
- **features** (hex, optional): features allowed for path
- **invoice\_created\_at** (u64): the UNIX timestamp of invoice creation
- **invoice\_payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters)
- **invoice\_amount\_msat** (msat): the amount required to fulfill invoice
- **signature** (bip340sig): BIP-340 signature of the `offer_node_id` on this invoice
- **offer\_id** (hex, optional): the id we use to identify this offer (always 64 characters)
- **offer\_chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
- the genesis blockhash (always 64 characters)
- **offer\_metadata** (hex, optional): any metadata the creator of the offer includes
- **offer\_currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters)
- **currency\_minor\_unit** (u32, optional): the number of decimal places to apply to amount (if currency known)
- **offer\_amount** (u64, optional): the amount in the `offer_currency` adjusted by `currency_minor_unit`, if any
- **offer\_amount\_msat** (msat, optional): the amount in bitcoin (if specified, and no `offer_currency`)
- **offer\_issuer** (string, optional): the description of the creator of the offer
- **offer\_features** (hex, optional): the feature bits of the offer
- **offer\_absolute\_expiry** (u64, optional): UNIX timestamp of when this offer expires
- **offer\_quantity\_max** (u64, optional): the maximum quantity (or, if 0, means any quantity)
- **offer\_paths** (array of objects, optional): Paths to the destination:
- **first\_node\_id** (pubkey): the (presumably well-known) public key of the start of the path
- **blinding** (pubkey): blinding factor for this path
- **path** (array of objects): an individual path:
- **blinded\_node\_id** (pubkey): node_id of the hop
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this hop
- **offer\_recurrence** (object, optional): how often to this offer should be used:
- **time\_unit** (u32): the BOLT12 time unit
- **period** (u32): how many `time_unit` per payment period
- **time\_unit\_name** (string, optional): the name of `time_unit` (if valid)
- **basetime** (u64, optional): period starts at this UNIX timestamp
- **start\_any\_period** (u64, optional): you can start at any period (only if `basetime` present)
- **limit** (u32, optional): maximum period number for recurrence
- **paywindow** (object, optional): when within a period will payment be accepted (default is prior and during the period):
- **seconds\_before** (u32): seconds prior to period start
- **seconds\_after** (u32): seconds after to period start
- **proportional\_amount** (boolean, optional): amount should be scaled if payed after period start (always *true*)
- **invreq\_chain** (hex, optional): which blockchain this offer is for (missing implies bitcoin mainnet only) (always 64 characters)
- **invreq\_amount\_msat** (msat, optional): the amount the invoice should be for
- **invreq\_features** (hex, optional): the feature bits of the invoice_request
- **invreq\_quantity** (u64, optional): the number of items to invoice for
- **invreq\_payer\_note** (string, optional): a note attached by the payer
- **invreq\_recurrence\_counter** (u32, optional): which number request this is for the same invoice
- **invreq\_recurrence\_start** (u32, optional): when we're requesting to start an invoice at a non-zero period
- **invoice\_relative\_expiry** (u32, optional): the number of seconds after *invoice_created_at* when this expires
- **invoice\_fallbacks** (array of objects, optional): onchain addresses:
- **version** (u8): Segwit address version
- **hex** (hex): Raw encoded segwit address
- **address** (string, optional): bech32 segwit address
- **refund\_signature** (bip340sig, optional): the payer key signature to get a refund
- **invoice\_features** (hex, optional): the feature bits of the invoice
- **invoice\_node\_id** (pubkey, optional): the id to pay (usually the same as offer_node_id)
- **invoice\_recurrence\_basetime** (u64, optional): the UNIX timestamp to base the invoice periods on
- the following warnings are possible:
- **warning\_unknown\_offer\_currency**: The currency code is unknown (so no `currency_minor_unit`)
If **type** is "bolt12 invoice", and **valid** is *false*:
- **fallbacks** (array of objects, optional):
- the following warnings are possible:
- **warning\_invoice\_fallbacks\_version\_invalid**: **version** is > 16
- **warning\_invoice\_fallbacks\_version\_invalid**: `version` is > 16
- the following warnings are possible:
- **warning\_invoice\_missing\_amount**: **amount_msat* missing
- **warning\_invoice\_missing\_description**: No **description**
- **warning\_invoice\_missing\_blinded\_payinfo**: Has **paths** without payinfo
- **warning\_invoice\_invalid\_blinded\_payinfo**: Does not have exactly one payinfo for each of **paths**
- **warning\_invoice\_missing\_recurrence\_basetime**: Has **recurrence_counter** without **recurrence_basetime**
- **warning\_invoice\_missing\_created\_at**: Missing **created_at**
- **warning\_invoice\_missing\_payment\_hash**: Missing **payment_hash**
- **warning\_invoice\_refund\_signature\_missing\_payer\_key**: Missing **payer_key** for refund_signature
- **warning\_invoice\_refund\_signature\_invalid**: **refund_signature** incorrect
- **warning\_invoice\_refund\_missing\_signature**: No **refund_signature**
If **type** is "bolt12 invoice_request", and **valid** is *true*:
- **offer\_id** (hex): the id of the offer this is requesting (merkle hash of non-signature fields) (always 64 characters)
- **payer\_key** (pubkey): the transient key which identifies the payer
- **chain** (hex, optional): which blockchain this invoice_request is for (missing implies bitcoin mainnet only) (always 64 characters)
- **amount\_msat** (msat, optional): the amount in bitcoin
- **features** (hex, optional): the array of feature bits for this offer
- **quantity** (u64, optional): the quantity ordered
- **recurrence\_counter** (u32, optional): the 0-based counter for a recurring payment
- **recurrence\_start** (u32, optional): the optional start period for a recurring payment
- **invreq\_metadata** (hex, optional): the payer-provided blob to derive payer_key
- **recurrence\_signature** (bip340sig, optional): the payer key signature
If **type** is "bolt12 invoice_request", and **valid** is *false*:
- the following warnings are possible:
- **warning\_invoice\_request\_missing\_offer\_id**: No **offer_id**
- **warning\_invoice\_request\_missing\_payer\_key**: No **payer_key**
- **warning\_invoice\_request\_missing\_recurrence\_signature**: No **recurrence_signature**
- **warning\_invoice\_request\_invalid\_recurrence\_signature**: **recurrence_signature** incorrect
- **warning\_invalid\_offer\_description**: `offer_description` is not valid UTF8
- **warning\_missing\_offer\_description**: `offer_description` is not present
- **warning\_invalid\_offer\_currency**: `offer_currency_code` is not valid UTF8
- **warning\_invalid\_offer\_issuer**: `offer_issuer` is not valid UTF8
- **warning\_missing\_invreq\_metadata**: `invreq_metadata` is not present
- **warning\_invalid\_invreq\_payer\_note**: `invreq_payer_note` is not valid UTF8
- **warning\_missing\_invoice\_paths**: `invoice_paths` is not present
- **warning\_missing\_invoice\_blindedpay**: `invoice_blindedpay` is not present
- **warning\_missing\_invoice\_created\_at**: `invoice_created_at` is not present
- **warning\_missing\_invoice\_payment\_hash**: `invoice_payment_hash` is not present
- **warning\_missing\_invoice\_amount**: `invoice_amount` is not present
- **warning\_missing\_invoice\_recurrence\_basetime**: `invoice_recurrence_basetime` is not present
- **warning\_missing\_invoice\_node\_id**: `invoice_node_id` is not present
- **warning\_missing\_invoice\_signature**: `signature` is not present
- **warning\_invalid\_invoice\_signature**: Incorrect `signature`
If **type** is "bolt11 invoice", and **valid** is *true*:
- **currency** (string): the BIP173 name for the currency
- **created\_at** (u64): the UNIX-style timestamp of the invoice
- **expiry** (u64): the number of seconds this is valid after *timestamp*
- **expiry** (u64): the number of seconds this is valid after `created_at`
- **payee** (pubkey): the public key of the recipient
- **payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters)
- **signature** (signature): signature of the *payee* on this invoice
@@ -215,4 +290,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:3f0c78dd665bff6749352801b0fdd958ee138fda6ede5b3631d9cb136fc45d76)
[comment]: # ( SHA256STAMP:e5791741d8b466b2f080dcde3e5a7770ce3a820d0b7e5635e6b6cfd1f104c09d)

View File

@@ -39,7 +39,7 @@ On success, an object is returned, containing:
If **bolt12** is present:
- **local\_offer\_id** (hex, optional): offer for which this invoice was created
- **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice
- **invreq\_payer\_note** (string, optional): the optional *invreq_payer_note* from invoice_request which created this invoice
If **status** is "paid":
@@ -81,4 +81,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:d754daa61ddb65009fced566338af35ffb23069593f4741e6d8f6f138f60bb4f)
[comment]: # ( SHA256STAMP:961571f6b2155f0452ac376bdf957474dd20e97e05a89efdf590f6e4da310f4f)

View File

@@ -32,7 +32,7 @@ On success, an object containing **invoices** is returned. It is an array of ob
- **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is)
- **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is)
- **local\_offer\_id** (hex, optional): the *id* of our offer which created this invoice (**experimental-offers** only). (always 64 characters)
- **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only).
- **invreq\_payer\_note** (string, optional): the optional *invreq_payer_note* from invoice_request which created this invoice (**experimental-offers** only).
If **status** is "paid":
@@ -58,4 +58,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:58de6b2fa9e3e796689618bf92a78dac66bb6cfe941d099abd73da3e41bfa60e)
[comment]: # ( SHA256STAMP:5c64a05bbf7485840010b16005c6f5d57725e4b0bf0a2a2106febe91ff0d4eb8)

View File

@@ -73,9 +73,9 @@
"maxLength": 64,
"minLength": 64
},
"payer_note": {
"invreq_payer_note": {
"type": "string",
"description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)."
"description": "the optional *invreq_payer_note* from invoice_request which created this invoice (**experimental-offers** only)."
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -79,9 +79,9 @@
"type": "hex",
"description": "offer for which this invoice was created"
},
"payer_note": {
"invreq_payer_note": {
"type": "string",
"description": "the optional *payer_note* from invoice_request which created this invoice"
"description": "the optional *invreq_payer_note* from invoice_request which created this invoice"
}
}
},
@@ -136,7 +136,7 @@
"amount_msat": {},
"description": {},
"payment_hash": {},
"payer_note": {},
"invreq_payer_note": {},
"local_offer_id": {},
"pay_index": {
"type": "u64",
@@ -174,7 +174,7 @@
"payment_hash": {},
"expires_at": {},
"pay_index": {},
"payer_note": {},
"invreq_payer_note": {},
"local_offer_id": {}
}
}

View File

@@ -66,9 +66,9 @@
"maxLength": 64,
"minLength": 64
},
"payer_note": {
"invreq_payer_note": {
"type": "string",
"description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)."
"description": "the optional *invreq_payer_note* from invoice_request which created this invoice (**experimental-offers** only)."
}
},
"allOf": [
@@ -101,7 +101,7 @@
"bolt11": {},
"bolt12": {},
"local_offer_id": {},
"payer_note": {},
"invreq_payer_note": {},
"expires_at": {},
"pay_index": {
"type": "u64",
@@ -138,7 +138,7 @@
"bolt11": {},
"bolt12": {},
"local_offer_id": {},
"payer_note": {},
"invreq_payer_note": {},
"expires_at": {}
}
}

View File

@@ -70,9 +70,8 @@ static void json_add_invoice_fields(struct json_stream *response,
tinv = invoice_decode(tmpctx,
inv->invstring, strlen(inv->invstring),
NULL, NULL, &fail);
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
if (tinv && tinv->invreq_payer_note)
json_add_stringn(response, "payer_note",
json_add_stringn(response, "invreq_payer_note",
tinv->invreq_payer_note,
tal_bytelen(tinv->invreq_payer_note));
}

View File

@@ -1136,7 +1136,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
invreq->invreq_features
= plugin_feature_set(cmd->plugin)->bits[BOLT12_OFFER_FEATURE];
/* invreq->payer_note is not a nul-terminated string! */
/* invreq->invreq_payer_note is not a nul-terminated string! */
if (payer_note)
invreq->invreq_payer_note = tal_dup_arr(invreq, utf8,
payer_note,

View File

@@ -230,9 +230,10 @@ static struct command_result *param_decodable(struct command *cmd,
}
static void json_add_chains(struct json_stream *js,
const char *fieldname,
const struct bitcoin_blkid *chains)
{
json_array_start(js, "chains");
json_array_start(js, fieldname);
for (size_t i = 0; i < tal_count(chains); i++)
json_add_sha256(js, NULL, &chains[i].shad.sha);
json_array_end(js);
@@ -247,7 +248,8 @@ static void json_add_onionmsg_path(struct json_stream *js,
json_add_pubkey(js, "blinded_node_id", &hop->blinded_node_id);
json_add_hex_talarr(js, "encrypted_recipient_data", hop->encrypted_recipient_data);
if (payinfo) {
json_add_u32(js, "fee_base_msat", payinfo->fee_base_msat);
json_add_amount_msat_only(js, "fee_base_msat",
amount_msat(payinfo->fee_base_msat));
json_add_u32(js, "fee_proportional_millionths",
payinfo->fee_proportional_millionths);
json_add_u32(js, "cltv_expiry_delta",
@@ -259,11 +261,12 @@ static void json_add_onionmsg_path(struct json_stream *js,
/* Returns true if valid */
static bool json_add_blinded_paths(struct json_stream *js,
const char *fieldname,
struct blinded_path **paths,
struct blinded_payinfo **blindedpay)
{
size_t n = 0;
json_array_start(js, "paths");
json_array_start(js, fieldname);
for (size_t i = 0; i < tal_count(paths); i++) {
json_object_start(js, NULL);
json_add_pubkey(js, "first_node_id", &paths[i]->first_node_id);
@@ -286,7 +289,7 @@ static bool json_add_blinded_paths(struct json_stream *js,
* `blinded_path`.
*/
if (blindedpay && n != tal_count(blindedpay)) {
json_add_string(js, "warning_invoice_invalid_blinded_payinfo",
json_add_string(js, "warning_invalid_invoice_blindedpay",
"invoice does not have correct number of blinded_payinfo");
return false;
}
@@ -312,33 +315,57 @@ static const char *recurrence_time_unit_name(u8 time_unit)
return NULL;
}
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer)
static bool json_add_utf8(struct json_stream *js,
const char *fieldname,
const char *utf8str)
{
if (utf8_check(utf8str, tal_bytelen(utf8str))) {
json_add_stringn(js, fieldname, utf8str, tal_bytelen(utf8str));
return true;
}
json_add_string(js, tal_fmt(tmpctx, "warning_invalid_%s", fieldname),
"invalid UTF8");
return false;
}
static bool json_add_offer_fields(struct json_stream *js,
const struct bitcoin_blkid *offer_chains,
const u8 *offer_metadata,
const char *offer_currency,
const u64 *offer_amount,
const char *offer_description,
const u8 *offer_features,
const u64 *offer_absolute_expiry,
struct blinded_path **offer_paths,
const char *offer_issuer,
const u64 *offer_quantity_max,
const struct pubkey *offer_node_id,
const struct recurrence *offer_recurrence,
const struct recurrence_paywindow *offer_recurrence_paywindow,
const u32 *offer_recurrence_limit,
const struct recurrence_base *offer_recurrence_base)
{
struct sha256 offer_id;
bool valid = true;
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
offer_offer_id(offer, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
if (offer->offer_chains)
json_add_chains(js, offer->offer_chains);
if (offer->offer_currency) {
if (offer_chains)
json_add_chains(js, "offer_chains", offer_chains);
if (offer_metadata)
json_add_hex_talarr(js, "offer_metadata", offer_metadata);
if (offer_currency) {
const struct iso4217_name_and_divisor *iso4217;
json_add_stringn(js, "currency",
offer->offer_currency,
tal_bytelen(offer->offer_currency));
if (offer->offer_amount)
json_add_u64(js, "amount", *offer->offer_amount);
iso4217 = find_iso4217(offer->offer_currency,
tal_bytelen(offer->offer_currency));
valid &= json_add_utf8(js, "offer_currency", offer_currency);
if (offer_amount)
json_add_u64(js, "offer_amount", *offer_amount);
iso4217 = find_iso4217(offer_currency,
tal_bytelen(offer_currency));
if (iso4217)
json_add_num(js, "minor_unit", iso4217->minor_unit);
json_add_num(js, "currency_minor_unit", iso4217->minor_unit);
else
json_add_string(js, "warning_offer_unknown_currency",
json_add_string(js, "warning_unknown_offer_currency",
"unknown currency code");
} else if (offer->offer_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*offer->offer_amount));
} else if (offer_amount)
json_add_amount_msat_only(js, "offer_amount_msat",
amount_msat(*offer_amount));
/* BOLT-offers #12:
* A reader of an offer:
@@ -346,71 +373,153 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
*/
if (offer->offer_description)
json_add_stringn(js, "description",
offer->offer_description,
tal_bytelen(offer->offer_description));
if (offer_description)
valid &= json_add_utf8(js, "offer_description",
offer_description);
else {
json_add_string(js, "warning_offer_missing_description",
json_add_string(js, "warning_missing_offer_description",
"offers without a description are invalid");
valid = false;
}
if (offer->offer_issuer)
json_add_stringn(js, "issuer", offer->offer_issuer,
tal_bytelen(offer->offer_issuer));
if (offer->offer_features)
json_add_hex_talarr(js, "features", offer->offer_features);
if (offer->offer_absolute_expiry)
json_add_u64(js, "absolute_expiry",
*offer->offer_absolute_expiry);
if (offer->offer_paths)
valid &= json_add_blinded_paths(js, offer->offer_paths, NULL);
if (offer_issuer)
valid &= json_add_utf8(js, "offer_issuer", offer_issuer);
if (offer_features)
json_add_hex_talarr(js, "offer_features", offer_features);
if (offer_absolute_expiry)
json_add_u64(js, "offer_absolute_expiry",
*offer_absolute_expiry);
if (offer_paths)
valid &= json_add_blinded_paths(js, "offer_paths",
offer_paths, NULL);
if (offer->offer_quantity_max)
json_add_u64(js, "quantity_max", *offer->offer_quantity_max);
if (offer->offer_recurrence) {
if (offer_quantity_max)
json_add_u64(js, "offer_quantity_max", *offer_quantity_max);
if (offer_recurrence) {
const char *name;
json_object_start(js, "recurrence");
json_add_num(js, "time_unit", offer->offer_recurrence->time_unit);
name = recurrence_time_unit_name(offer->offer_recurrence->time_unit);
json_object_start(js, "offer_recurrence");
json_add_num(js, "time_unit", offer_recurrence->time_unit);
name = recurrence_time_unit_name(offer_recurrence->time_unit);
if (name)
json_add_string(js, "time_unit_name", name);
json_add_num(js, "period", offer->offer_recurrence->period);
if (offer->offer_recurrence_base) {
json_add_num(js, "period", offer_recurrence->period);
if (offer_recurrence_base) {
json_add_u64(js, "basetime",
offer->offer_recurrence_base->basetime);
if (offer->offer_recurrence_base->start_any_period)
offer_recurrence_base->basetime);
if (offer_recurrence_base->start_any_period)
json_add_bool(js, "start_any_period", true);
}
if (offer->offer_recurrence_limit)
json_add_u32(js, "limit", *offer->offer_recurrence_limit);
if (offer->offer_recurrence_paywindow) {
if (offer_recurrence_limit)
json_add_u32(js, "limit", *offer_recurrence_limit);
if (offer_recurrence_paywindow) {
json_object_start(js, "paywindow");
json_add_u32(js, "seconds_before",
offer->offer_recurrence_paywindow->seconds_before);
offer_recurrence_paywindow->seconds_before);
json_add_u32(js, "seconds_after",
offer->offer_recurrence_paywindow->seconds_after);
if (offer->offer_recurrence_paywindow->proportional_amount)
offer_recurrence_paywindow->seconds_after);
if (offer_recurrence_paywindow->proportional_amount)
json_add_bool(js, "proportional_amount", true);
json_object_end(js);
}
json_object_end(js);
}
/* Required for offers, *not* for others! */
if (offer_node_id)
json_add_pubkey(js, "offer_node_id", offer_node_id);
return valid;
}
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer)
{
struct sha256 offer_id;
bool valid = true;
offer_offer_id(offer, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
valid &= json_add_offer_fields(js,
offer->offer_chains,
offer->offer_metadata,
offer->offer_currency,
offer->offer_amount,
offer->offer_description,
offer->offer_features,
offer->offer_absolute_expiry,
offer->offer_paths,
offer->offer_issuer,
offer->offer_quantity_max,
offer->offer_node_id,
offer->offer_recurrence,
offer->offer_recurrence_paywindow,
offer->offer_recurrence_limit,
offer->offer_recurrence_base);
/* BOLT-offers #12:
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
if (offer->offer_node_id)
json_add_pubkey(js, "node_id", offer->offer_node_id);
else
if (!offer->offer_node_id) {
json_add_string(js, "warning_missing_offer_node_id",
"offers without a node_id are invalid");
valid = false;
}
json_add_bool(js, "valid", valid);
}
static bool json_add_invreq_fields(struct json_stream *js,
const u8 *invreq_metadata,
const struct bitcoin_blkid *invreq_chain,
const u64 *invreq_amount,
const u8 *invreq_features,
const u64 *invreq_quantity,
const struct pubkey *invreq_payer_id,
const utf8 *invreq_payer_note,
const u32 *invreq_recurrence_counter,
const u32 *invreq_recurrence_start)
{
bool valid = true;
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata` are not present.
*/
if (invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata",
invreq_metadata);
else {
json_add_string(js, "warning_missing_invreq_metadata",
"invreq_metadata required");
valid = false;
}
/* This can be missing for an invoice though! */
if (invreq_payer_id)
json_add_pubkey(js, "invreq_payer_id", invreq_payer_id);
if (invreq_chain)
json_add_sha256(js, "invreq_chain", &invreq_chain->shad.sha);
if (invreq_amount)
json_add_amount_msat_only(js, "invreq_amount_msat",
amount_msat(*invreq_amount));
if (invreq_features)
json_add_hex_talarr(js, "invreq_features", invreq_features);
if (invreq_quantity)
json_add_u64(js, "invreq_quantity", *invreq_quantity);
if (invreq_payer_note)
valid &= json_add_utf8(js, "invreq_payer_note", invreq_payer_note);
if (invreq_recurrence_counter) {
json_add_u32(js, "invreq_recurrence_counter",
*invreq_recurrence_counter);
if (invreq_recurrence_start)
json_add_u32(js, "invreq_recurrence_start",
*invreq_recurrence_start);
}
return valid;
}
/* Returns true if valid */
static bool json_add_fallback_address(struct json_stream *js,
const struct chainparams *chain,
@@ -425,7 +534,7 @@ static bool json_add_fallback_address(struct json_stream *js,
return true;
}
json_add_string(js,
"warning_invoice_fallbacks_address_invalid",
"warning_invalid_invoice_fallbacks_address",
"invalid fallback address for this version");
return false;
}
@@ -444,7 +553,7 @@ static bool json_add_fallbacks(struct json_stream *js,
else
chain = chainparams_for_network("bitcoin");
json_array_start(js, "fallbacks");
json_array_start(js, "invoice_fallbacks");
for (size_t i = 0; i < tal_count(fallbacks); i++) {
size_t addrlen = tal_bytelen(fallbacks[i]->address);
@@ -463,12 +572,12 @@ static bool json_add_fallbacks(struct json_stream *js,
*/
if (fallbacks[i]->version > 16) {
json_add_string(js,
"warning_invoice_fallbacks_version_invalid",
"warning_invalid_invoice_fallbacks_version",
"invoice fallback version > 16");
valid = false;
} else if (addrlen < 2 || addrlen > 40) {
json_add_string(js,
"warning_invoice_fallbacks_address_invalid",
"warning_invalid_invoice_fallbacks_address",
"invoice fallback address bad length");
valid = false;
} else if (chain) {
@@ -483,6 +592,82 @@ static bool json_add_fallbacks(struct json_stream *js,
return valid;
}
static void json_add_invoice_request(struct json_stream *js,
const struct tlv_invoice_request *invreq)
{
bool valid = true;
/* If there's an offer_node_id, then there's an offer. */
if (invreq->offer_node_id) {
struct sha256 offer_id;
invreq_offer_id(invreq, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
valid &= json_add_offer_fields(js,
invreq->offer_chains,
invreq->offer_metadata,
invreq->offer_currency,
invreq->offer_amount,
invreq->offer_description,
invreq->offer_features,
invreq->offer_absolute_expiry,
invreq->offer_paths,
invreq->offer_issuer,
invreq->offer_quantity_max,
invreq->offer_node_id,
invreq->offer_recurrence,
invreq->offer_recurrence_paywindow,
invreq->offer_recurrence_limit,
invreq->offer_recurrence_base);
valid &= json_add_invreq_fields(js,
invreq->invreq_metadata,
invreq->invreq_chain,
invreq->invreq_amount,
invreq->invreq_features,
invreq->invreq_quantity,
invreq->invreq_payer_id,
invreq->invreq_payer_note,
invreq->invreq_recurrence_counter,
invreq->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata` are not present.
*/
if (!invreq->invreq_payer_id) {
json_add_string(js, "warning_missing_invreq_payer_id",
"invreq_payer_id required");
valid = false;
}
/* BOLT-offers #12:
* - MUST fail the request if `signature` is not correct as detailed
* in [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
*/
if (invreq->signature) {
if (invreq->invreq_payer_id
&& !bolt12_check_signature(invreq->fields,
"invoice_request",
"signature",
invreq->invreq_payer_id,
invreq->signature)) {
json_add_string(js, "warning_invalid_invoice_request_signature",
"Bad signature");
valid = false;
} else {
json_add_bip340sig(js, "signature", invreq->signature);
}
} else {
json_add_string(js, "warning_missing_invoice_request_signature",
"Missing signature");
valid = false;
}
json_add_bool(js, "valid", valid);
}
static void json_add_b12_invoice(struct json_stream *js,
const struct tlv_invoice *invoice)
{
@@ -496,66 +681,32 @@ static void json_add_b12_invoice(struct json_stream *js,
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invoice_ as per spec */
if (invoice->invreq_chain)
json_add_sha256(js, "chain", &invoice->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_amount` is not present.
* - MUST reject the invoice if `invreq_payer_id` is not present.
*/
if (invoice->invoice_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invoice->invoice_amount));
else {
json_add_string(js, "warning_invoice_missing_amount",
"invoices without an amount are invalid");
valid = false;
}
if (invoice->invreq_payer_id)
json_add_pubkey(js, "payer_key", invoice->invreq_payer_id);
else {
json_add_string(js, "warning_invoice_missing_invreq_payer_id",
"invoices without an invreq_payer_id are invald");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `offer_description` is not present.
* - MUST reject the invoice if `invoice_created_at` is not present.
* - MUST reject the invoice if `invoice_payment_hash` is not present.
*/
if (invoice->offer_description)
json_add_stringn(js, "description", invoice->offer_description,
tal_bytelen(invoice->offer_description));
else {
json_add_string(js, "warning_invoice_missing_description",
"invoices without a description are invalid");
valid = false;
}
if (invoice->invoice_created_at) {
json_add_u64(js, "created_at", *invoice->invoice_created_at);
} else {
json_add_string(js, "warning_invoice_missing_created_at",
"invoices without created_at are invalid");
valid = false;
}
if (invoice->invoice_payment_hash)
json_add_sha256(js, "payment_hash", invoice->invoice_payment_hash);
else {
json_add_string(js, "warning_invoice_missing_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
if (invoice->offer_issuer)
json_add_stringn(js, "issuer", invoice->offer_issuer,
tal_bytelen(invoice->offer_issuer));
if (invoice->invoice_features)
json_add_hex_talarr(js, "features", invoice->invoice_features);
valid &= json_add_offer_fields(js,
invoice->offer_chains,
invoice->offer_metadata,
invoice->offer_currency,
invoice->offer_amount,
invoice->offer_description,
invoice->offer_features,
invoice->offer_absolute_expiry,
invoice->offer_paths,
invoice->offer_issuer,
invoice->offer_quantity_max,
invoice->offer_node_id,
invoice->offer_recurrence,
invoice->offer_recurrence_paywindow,
invoice->offer_recurrence_limit,
invoice->offer_recurrence_base);
valid &= json_add_invreq_fields(js,
invoice->invreq_metadata,
invoice->invreq_chain,
invoice->invreq_amount,
invoice->invreq_features,
invoice->invreq_quantity,
invoice->invreq_payer_id,
invoice->invreq_payer_note,
invoice->invreq_recurrence_counter,
invoice->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_paths` is not present
@@ -574,47 +725,26 @@ static void json_add_b12_invoice(struct json_stream *js,
* in `blinded_path`.
*/
if (!invoice->invoice_blindedpay) {
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
"invoices with blinded_path without blinded_payinfo are invalid");
json_add_string(js, "warning_missing_invoice_blindedpay",
"invoices with paths without blindedpay are invalid");
valid = false;
}
valid &= json_add_blinded_paths(js, invoice->invoice_paths,
valid &= json_add_blinded_paths(js, "invoice_paths",
invoice->invoice_paths,
invoice->invoice_blindedpay);
} else {
json_add_string(js, "warning_invoice_missing_blinded_path",
"invoices without a payment_hash are invalid");
json_add_string(js, "warning_missing_invoice_paths",
"invoices without a invoice_paths are invalid");
valid = false;
}
if (invoice->invreq_quantity)
json_add_u64(js, "quantity", *invoice->invreq_quantity);
if (invoice->invreq_recurrence_counter) {
json_add_u32(js, "recurrence_counter",
*invoice->invreq_recurrence_counter);
if (invoice->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invoice->invreq_recurrence_start);
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not
* set.
*/
if (invoice->invoice_recurrence_basetime)
json_add_u64(js, "recurrence_basetime",
*invoice->invoice_recurrence_basetime);
else {
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
"recurring invoices without a recurrence_basetime are invalid");
if (invoice->invoice_created_at) {
json_add_u64(js, "invoice_created_at", *invoice->invoice_created_at);
} else {
json_add_string(js, "warning_missing_invoice_created_at",
"invoices without created_at are invalid");
valid = false;
}
}
if (invoice->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata",
invoice->invreq_metadata);
if (invoice->invreq_payer_note)
json_add_stringn(js, "payer_note", invoice->invreq_payer_note,
tal_bytelen(invoice->invreq_payer_note));
/* BOLT-offers #12:
*
@@ -626,107 +756,68 @@ static void json_add_b12_invoice(struct json_stream *js,
* is greater than `invoice_created_at` plus 7200.
*/
if (invoice->invoice_relative_expiry)
json_add_u32(js, "relative_expiry", *invoice->invoice_relative_expiry);
json_add_u32(js, "invoice_relative_expiry", *invoice->invoice_relative_expiry);
else
json_add_u32(js, "relative_expiry", 7200);
json_add_u32(js, "invoice_relative_expiry", BOLT12_DEFAULT_REL_EXPIRY);
if (invoice->invoice_payment_hash)
json_add_sha256(js, "invoice_payment_hash", invoice->invoice_payment_hash);
else {
json_add_string(js, "warning_missing_invoice_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_amount` is not present.
*/
if (invoice->invoice_amount)
json_add_amount_msat_only(js, "invoice_amount_msat",
amount_msat(*invoice->invoice_amount));
else {
json_add_string(js, "warning_missing_invoice_amount",
"invoices without an amount are invalid");
valid = false;
}
if (invoice->invoice_fallbacks)
valid &= json_add_fallbacks(js,
invoice->invreq_chain,
invoice->invoice_fallbacks);
/* invoice_decode checked these */
json_add_pubkey(js, "node_id", invoice->offer_node_id);
if (invoice->invoice_features)
json_add_hex_talarr(js, "features", invoice->invoice_features);
if (invoice->invoice_node_id)
json_add_pubkey(js, "invoice_node_id", invoice->invoice_node_id);
else {
json_add_string(js, "warning_missing_invoice_node_id",
"invoices without an invoice_node_id are invalid");
valid = false;
}
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not
* set.
*/
if (invoice->offer_recurrence) {
if (invoice->invoice_recurrence_basetime)
json_add_u64(js, "invoice_recurrence_basetime",
*invoice->invoice_recurrence_basetime);
else {
json_add_string(js, "warning_missing_invoice_recurrence_basetime",
"recurring invoices without a recurrence_basetime are invalid");
valid = false;
}
}
/* invoice_decode checked this */
json_add_bip340sig(js, "signature", invoice->signature);
json_add_bool(js, "valid", valid);
}
static void json_add_invoice_request(struct json_stream *js,
const struct tlv_invoice_request *invreq)
{
bool valid = true;
/* If there's an offer_node_id, then there's an offer. */
if (invreq->offer_node_id) {
struct sha256 offer_id;
invreq_offer_id(invreq, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invreq_ as per spec */
if (invreq->invreq_chain)
json_add_sha256(js, "chain", &invreq->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST fail the request if `payer_key` is not present.
*...
* - MUST fail the request if `features` contains unknown even bits.
* - MUST fail the request if `offer_id` is not present.
*/
if (invreq->invreq_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invreq->invreq_amount));
if (invreq->invreq_features)
json_add_hex_talarr(js, "features", invreq->invreq_features);
if (invreq->invreq_quantity)
json_add_u64(js, "quantity", *invreq->invreq_quantity);
if (invreq->invreq_recurrence_counter)
json_add_u32(js, "recurrence_counter",
*invreq->invreq_recurrence_counter);
if (invreq->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invreq->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata`
* are not present.
*/
if (invreq->invreq_payer_id)
json_add_pubkey(js, "payer_key", invreq->invreq_payer_id);
else {
json_add_string(js, "warning_invoice_request_missing_payer_key",
"invoice_request requires payer_key");
valid = false;
}
if (invreq->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata", invreq->invreq_metadata);
else {
json_add_string(js, "warning_invoice_request_missing_invreq_metadata",
"invoice_request requires invreq_metadata");
valid = false;
}
if (invreq->invreq_payer_note)
json_add_stringn(js, "payer_note", invreq->invreq_payer_note,
tal_bytelen(invreq->invreq_payer_note));
/* BOLT-offers #12:
* - MUST fail the request if `signature` is not correct as detailed
* in [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
*/
if (invreq->signature) {
if (invreq->invreq_payer_id
&& !bolt12_check_signature(invreq->fields,
"invoice_request",
"signature",
invreq->invreq_payer_id,
invreq->signature)) {
json_add_string(js, "warning_invoice_request_invalid_signature",
"Bad signature");
valid = false;
}
} else {
json_add_string(js, "warning_invoice_request_missing_signature",
"Missing signature");
valid = false;
}
json_add_bool(js, "valid", valid);
}
static void json_add_rune(struct command *cmd, struct json_stream *js, const struct rune *rune)
{
const char *string;

View File

@@ -4592,15 +4592,15 @@ def test_fetchinvoice(node_factory, bitcoind):
# listinvoices will show these on l3
assert [x['local_offer_id'] for x in l3.rpc.listinvoices()['invoices']] == [offer1['offer_id'], offer1['offer_id']]
assert 'payer_note' not in only_one(l3.rpc.call('listinvoices', {'invstring': inv1['invoice']})['invoices'])
assert only_one(l3.rpc.call('listinvoices', {'invstring': inv2['invoice']})['invoices'])['payer_note'] == 'Thanks for the fish!'
assert 'invreq_payer_note' not in only_one(l3.rpc.call('listinvoices', {'invstring': inv1['invoice']})['invoices'])
assert only_one(l3.rpc.call('listinvoices', {'invstring': inv2['invoice']})['invoices'])['invreq_payer_note'] == 'Thanks for the fish!'
# BTW, test listinvoices-by-offer_id:
assert len(l3.rpc.listinvoices(offer_id=offer1['offer_id'])['invoices']) == 2
# We can also set the amount explicitly, to tip.
inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'amount_msat': 3})
assert l1.rpc.call('decode', [inv1['invoice']])['amount_msat'] == 3
assert l1.rpc.call('decode', [inv1['invoice']])['invoice_amount_msat'] == 3
l1.rpc.pay(inv1['invoice'])
# More than ~5x expected is rejected as absurd (it's actually a divide test,
@@ -5222,4 +5222,4 @@ def test_payerkey(node_factory):
for n, k in zip(nodes, expected_keys):
b12 = n.rpc.createinvoicerequest('lnr1qqgz2d7u2smys9dc5q2447e8thjlgq3qqc3xu3s3rg94nj40zfsy866mhu5vxne6tcej5878k2mneuvgjy8ssqvepgz5zsjrg3z3vggzvkm2khkgvrxj27r96c00pwl4kveecdktm29jdd6w0uwu5jgtv5v9qgqxyfhyvyg6pdvu4tcjvpp7kkal9rp57wj7xv4pl3ajku70rzy3pu')['bolt12']
assert n.rpc.decode(b12)['payer_key'] == k
assert n.rpc.decode(b12)['invreq_payer_id'] == k