offers: signatures are now optional.

As per latest spec revision.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: BOLT12 offers can now be unsigned, for really short QR codes.
This commit is contained in:
Rusty Russell
2021-07-02 09:41:34 +09:30
parent 6336d39a95
commit f9fe814ea3
20 changed files with 130 additions and 61 deletions

View File

@@ -166,27 +166,6 @@ struct tlv_offer *offer_decode(const tal_t *ctx,
const struct feature_set *our_features, const struct feature_set *our_features,
const struct chainparams *must_be_chain, const struct chainparams *must_be_chain,
char **fail) char **fail)
{
struct tlv_offer *offer;
offer = offer_decode_nosig(ctx, b12, b12len,
our_features, must_be_chain, fail);
if (offer) {
*fail = check_signature(ctx, offer->fields,
"offer", "signature",
offer->node_id, offer->signature);
if (*fail)
offer = tal_free(offer);
}
return offer;
}
struct tlv_offer *offer_decode_nosig(const tal_t *ctx,
const char *b12, size_t b12len,
const struct feature_set *our_features,
const struct chainparams *must_be_chain,
char **fail)
{ {
struct tlv_offer *offer = tlv_offer_new(ctx); struct tlv_offer *offer = tlv_offer_new(ctx);
const u8 *data; const u8 *data;
@@ -208,6 +187,19 @@ struct tlv_offer *offer_decode_nosig(const tal_t *ctx,
if (*fail) if (*fail)
return tal_free(offer); return tal_free(offer);
/* BOLT-offers #12:
* - if `signature` is present, but is not a valid signature using
* `node_id` as described in [Signature Calculation](#signature-calculation):
* - MUST NOT respond to the offer.
*/
if (offer->signature) {
*fail = check_signature(ctx, offer->fields,
"offer", "signature",
offer->node_id, offer->signature);
if (*fail)
return tal_free(offer);
}
return offer; return offer;
} }

View File

@@ -33,20 +33,13 @@ char *offer_encode(const tal_t *ctx, const struct tlv_offer *bolt12_tlv);
* @must_be_chain: if non-NULL, chain to enforce. * @must_be_chain: if non-NULL, chain to enforce.
* @fail: pointer to descriptive error string, set if this returns NULL. * @fail: pointer to descriptive error string, set if this returns NULL.
* *
* Note: checks signature! * Note: checks signature if present.
*/ */
struct tlv_offer *offer_decode(const tal_t *ctx, const char *b12, size_t b12len, struct tlv_offer *offer_decode(const tal_t *ctx, const char *b12, size_t b12len,
const struct feature_set *our_features, const struct feature_set *our_features,
const struct chainparams *must_be_chain, const struct chainparams *must_be_chain,
char **fail); char **fail);
/* Variant which does not check signature */
struct tlv_offer *offer_decode_nosig(const tal_t *ctx,
const char *b12, size_t b12len,
const struct feature_set *our_features,
const struct chainparams *must_be_chain,
char **fail);
/** /**
* invrequest_encode - encode this complete bolt12 invreq TLV into text. * invrequest_encode - encode this complete bolt12 invreq TLV into text.
*/ */

View File

@@ -481,7 +481,7 @@ int main(int argc, char *argv[])
if (streq(hrp, "lno")) { if (streq(hrp, "lno")) {
const struct tlv_offer *offer const struct tlv_offer *offer
= offer_decode_nosig(ctx, argv[2], strlen(argv[2]), = offer_decode(ctx, argv[2], strlen(argv[2]),
NULL, NULL, &fail); NULL, NULL, &fail);
if (!offer) if (!offer)
errx(ERROR_BAD_DECODE, "Bad offer: %s", fail); errx(ERROR_BAD_DECODE, "Bad offer: %s", fail);
@@ -517,7 +517,7 @@ int main(int argc, char *argv[])
print_features(offer->features); print_features(offer->features);
if (offer->paths) if (offer->paths)
print_blindedpaths(offer->paths, NULL); print_blindedpaths(offer->paths, NULL);
if (must_have(offer, signature) && offer->node_id) if (offer->signature && offer->node_id)
well_formed &= print_signature("offer", "signature", well_formed &= print_signature("offer", "signature",
offer->fields, offer->fields,
offer->node_id, offer->node_id,

View File

@@ -35,10 +35,10 @@ If \fBtype\fR is "bolt12 offer", and \fBvalid\fR is \fItrue\fR:
.IP \[bu] .IP \[bu]
\fBnode_id\fR (pubkey32): x-only public key of the offering node \fBnode_id\fR (pubkey32): x-only public key of the offering node
.IP \[bu] .IP \[bu]
\fBsignature\fR (bip340sig): BIP-340 signature of the \fInode_id\fR on this offer
.IP \[bu]
\fBdescription\fR (string): the description of the purpose of the offer \fBdescription\fR (string): the description of the purpose of the offer
.IP \[bu] .IP \[bu]
\fBsignature\fR (bip340sig, optional): BIP-340 signature of the \fInode_id\fR on this offer
.IP \[bu]
\fBchains\fR (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only): \fBchains\fR (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
.RS .RS
.IP \[bu] .IP \[bu]
@@ -414,4 +414,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:045cd00ad893483df132bdffd913049bfe43acb152a511accc9f17f87ba06a8d \" SHA256STAMP:cd54af7c631f06b3db72848cdf90951ceb14d89b8bca981dba69244cd2ddbae5

View File

@@ -27,8 +27,8 @@ On success, an object is returned, containing:
If **type** is "bolt12 offer", and **valid** is *true*: 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) - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters)
- **node_id** (pubkey32): x-only public key of the offering node - **node_id** (pubkey32): x-only public key of the offering node
- **signature** (bip340sig): BIP-340 signature of the *node_id* on this offer
- **description** (string): the description of the purpose of the offer - **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): - **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
- the genesis blockhash (always 64 characters) - the genesis blockhash (always 64 characters)
- **currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters) - **currency** (string, optional): ISO 4217 code of the currency (missing implies Bitcoin) (always 3 characters)
@@ -183,4 +183,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:f0adee97f3b5776059252703efee1b8e244c1141f9f3dd5fe73e3d7ed4d59ab4) [comment]: # ( SHA256STAMP:8ca0b9178b8ea6575cd80291001263dc27f721664648086a7c1a02efcb545ee7)

View File

@@ -48,6 +48,8 @@ On success, an object is returned, containing:
.IP \[bu] .IP \[bu]
\fBbolt12\fR (string): The bolt12 string representing this offer \fBbolt12\fR (string): The bolt12 string representing this offer
.IP \[bu] .IP \[bu]
\fBbolt12_unsigned\fR (string): The bolt12 string representing this offer, without signature
.IP \[bu]
\fBused\fR (boolean): Whether the offer has had an invoice paid / payment made \fBused\fR (boolean): Whether the offer has had an invoice paid / payment made
.IP \[bu] .IP \[bu]
\fBlabel\fR (string, optional): The label provided when offer was created \fBlabel\fR (string, optional): The label provided when offer was created
@@ -78,4 +80,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:03b1a12409ad02eca1543d99bafdfbd2d7d2c869e182fba513e3f93fb48a7664 \" SHA256STAMP:2383f44a3bcc1023d6619f5ead4c27d7b2bd9a52bd64d00142fd26658a49dd32

View File

@@ -40,6 +40,7 @@ On success, an object is returned, containing:
- **active** (boolean): Whether the offer can produce invoices/payments (always "false") - **active** (boolean): Whether the offer can produce invoices/payments (always "false")
- **single_use** (boolean): Whether the offer is disabled after first successful use - **single_use** (boolean): Whether the offer is disabled after first successful use
- **bolt12** (string): The bolt12 string representing this offer - **bolt12** (string): The bolt12 string representing this offer
- **bolt12_unsigned** (string): The bolt12 string representing this offer, without signature
- **used** (boolean): Whether the offer has had an invoice paid / payment made - **used** (boolean): Whether the offer has had an invoice paid / payment made
- **label** (string, optional): The label provided when offer was created - **label** (string, optional): The label provided when offer was created
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -72,4 +73,4 @@ RESOURCES
--------- ---------
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:abf340bf35dcefd42fba609b3ae95adb2f74bb5766c68e174a9b8f9114c80202) [comment]: # ( SHA256STAMP:5b96eca3e35f6c556b93db1743c617b59e69058c9421ece9cc99a9c8814c176b)

View File

@@ -41,6 +41,8 @@ On success, an object containing \fBoffers\fR is returned\. It is an array of o
.IP \[bu] .IP \[bu]
\fBbolt12\fR (string): the bolt12 encoding of the offer \fBbolt12\fR (string): the bolt12 encoding of the offer
.IP \[bu] .IP \[bu]
\fBbolt12_unsigned\fR (string): the bolt12 encoding of the offer, without signature
.IP \[bu]
\fBused\fR (boolean): True if an associated invoice has been paid \fBused\fR (boolean): True if an associated invoice has been paid
.IP \[bu] .IP \[bu]
\fBlabel\fR (string, optional): the (optional) user-specified label \fBlabel\fR (string, optional): the (optional) user-specified label
@@ -82,4 +84,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:7e359219084e648a629b0d43774db17bbfbe693074b817fa5890b7c8bccd1429 \" SHA256STAMP:bf83e52cc56aaef876eb1039a6fbbcd9d2e15703c54f33163176c7ba41523261

View File

@@ -35,6 +35,7 @@ On success, an object containing **offers** is returned. It is an array of obje
- **active** (boolean): whether this can still be used - **active** (boolean): whether this can still be used
- **single_use** (boolean): whether this expires as soon as it's paid - **single_use** (boolean): whether this expires as soon as it's paid
- **bolt12** (string): the bolt12 encoding of the offer - **bolt12** (string): the bolt12 encoding of the offer
- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without signature
- **used** (boolean): True if an associated invoice has been paid - **used** (boolean): True if an associated invoice has been paid
- **label** (string, optional): the (optional) user-specified label - **label** (string, optional): the (optional) user-specified label
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -78,4 +79,4 @@ RESOURCES
--------- ---------
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:58e9f5aa5808e19e3be151b1c1f1215ec23953b60ab294418aa57426c5bcbd46) [comment]: # ( SHA256STAMP:e2b8508c98e6161c45fca91bceb273e6c2865cec0e1761dde85f8f2dd6670491)

9
doc/lightning-offer.7 generated
View File

@@ -15,6 +15,11 @@ creating one or more invoices\. It automatically enables the processing of
an incoming invoice_request, and issuing of invoices\. an incoming invoice_request, and issuing of invoices\.
Note that it creates two variants of the offer: a signed and an
unsigned one (which is smaller)\. Wallets should accept both: the
current specification allows either\.
The \fIamount\fR parameter can be the string "any", which creates an offer The \fIamount\fR parameter can be the string "any", which creates an offer
that can be paid with any amount (e\.g\. a donation)\. Otherwise it can that can be paid with any amount (e\.g\. a donation)\. Otherwise it can
be a positive value in millisatoshi precision; it can be a whole be a positive value in millisatoshi precision; it can be a whole
@@ -112,6 +117,8 @@ On success, an object is returned, containing:
.IP \[bu] .IP \[bu]
\fBbolt12\fR (string): the bolt12 encoding of the offer \fBbolt12\fR (string): the bolt12 encoding of the offer
.IP \[bu] .IP \[bu]
\fBbolt12_unsigned\fR (string): the bolt12 encoding of the offer, without a signature
.IP \[bu]
\fBused\fR (boolean): True if an associated invoice has been paid (always \fIfalse\fR) \fBused\fR (boolean): True if an associated invoice has been paid (always \fIfalse\fR)
.IP \[bu] .IP \[bu]
\fBlabel\fR (string, optional): the (optional) user-specified label \fBlabel\fR (string, optional): the (optional) user-specified label
@@ -145,4 +152,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:fdc65b544ee660ef7afafb13bc727b859411df072739c4b22973fbc870d785f2 \" SHA256STAMP:f7faf86c7cb052200c3b4d6e3d6209afa54600cc7a33e1082583a2766d02a275

View File

@@ -15,6 +15,10 @@ The **offer** RPC command creates an offer, which is a precursor to
creating one or more invoices. It automatically enables the processing of creating one or more invoices. It automatically enables the processing of
an incoming invoice_request, and issuing of invoices. an incoming invoice_request, and issuing of invoices.
Note that it creates two variants of the offer: a signed and an
unsigned one (which is smaller). Wallets should accept both: the
current specification allows either.
The *amount* parameter can be the string "any", which creates an offer The *amount* parameter can be the string "any", which creates an offer
that can be paid with any amount (e.g. a donation). Otherwise it can that can be paid with any amount (e.g. a donation). Otherwise it can
be a positive value in millisatoshi precision; it can be a whole be a positive value in millisatoshi precision; it can be a whole
@@ -95,6 +99,7 @@ On success, an object is returned, containing:
- **active** (boolean): whether this can still be used (always *true*) - **active** (boolean): whether this can still be used (always *true*)
- **single_use** (boolean): whether this expires as soon as it's paid (reflects the *single_use* parameter) - **single_use** (boolean): whether this expires as soon as it's paid (reflects the *single_use* parameter)
- **bolt12** (string): the bolt12 encoding of the offer - **bolt12** (string): the bolt12 encoding of the offer
- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without a signature
- **used** (boolean): True if an associated invoice has been paid (always *false*) - **used** (boolean): True if an associated invoice has been paid (always *false*)
- **label** (string, optional): the (optional) user-specified label - **label** (string, optional): the (optional) user-specified label
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -123,4 +128,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:20c0be7bad73fcf71ccae61c2c5a112c8602216d9d2e9f647f8273fdf4e3ed8b) [comment]: # ( SHA256STAMP:0f9cfd3cc68aaba20af0eee763c93b475016619d960e3f5bbc0b762a809f0fef)

View File

@@ -17,6 +17,11 @@ offer)\. It automatically enables the accepting and payment of
corresponding invoice message (we will only pay once, however!)\. corresponding invoice message (we will only pay once, however!)\.
Note that it creates two variants of the offer: a signed and an
unsigned one (which is smaller)\. Wallets should accept both: the
current specification allows either\.
The \fIamount\fR parameter can be the string "any", which creates an offer The \fIamount\fR parameter can be the string "any", which creates an offer
that can be paid with any amount (e\.g\. a donation)\. Otherwise it can that can be paid with any amount (e\.g\. a donation)\. Otherwise it can
be a positive value in millisatoshi precision; it can be a whole be a positive value in millisatoshi precision; it can be a whole
@@ -64,6 +69,8 @@ On success, an object is returned, containing:
.IP \[bu] .IP \[bu]
\fBbolt12\fR (string): the bolt12 encoding of the offer \fBbolt12\fR (string): the bolt12 encoding of the offer
.IP \[bu] .IP \[bu]
\fBbolt12_unsigned\fR (string): the bolt12 encoding of the offer, without a signature
.IP \[bu]
\fBused\fR (boolean): True if an incoming invoice has been paid (always \fIfalse\fR) \fBused\fR (boolean): True if an incoming invoice has been paid (always \fIfalse\fR)
.IP \[bu] .IP \[bu]
\fBlabel\fR (string, optional): the (optional) user-specified label \fBlabel\fR (string, optional): the (optional) user-specified label
@@ -106,4 +113,4 @@ Rusty Russell \fI<rusty@rustcorp.com.au\fR> is mainly responsible\.
Main web site: \fIhttps://github.com/ElementsProject/lightning\fR Main web site: \fIhttps://github.com/ElementsProject/lightning\fR
\" SHA256STAMP:823219aff5dc06ab3b810442048b6cf733210c3eae80567327dc396e5f7987c8 \" SHA256STAMP:3de11c6de7905322d9ef748981fc1d4f9ca91f4be46d76af6e9124572853047d

View File

@@ -18,6 +18,10 @@ send an invoice for us to pay (technically, this is referred to as a
offer). It automatically enables the accepting and payment of offer). It automatically enables the accepting and payment of
corresponding invoice message (we will only pay once, however!). corresponding invoice message (we will only pay once, however!).
Note that it creates two variants of the offer: a signed and an
unsigned one (which is smaller). Wallets should accept both: the
current specification allows either.
The *amount* parameter can be the string "any", which creates an offer The *amount* parameter can be the string "any", which creates an offer
that can be paid with any amount (e.g. a donation). Otherwise it can that can be paid with any amount (e.g. a donation). Otherwise it can
be a positive value in millisatoshi precision; it can be a whole be a positive value in millisatoshi precision; it can be a whole
@@ -55,6 +59,7 @@ On success, an object is returned, containing:
- **active** (boolean): whether this will pay a matching incoming invoice (always *true*) - **active** (boolean): whether this will pay a matching incoming invoice (always *true*)
- **single_use** (boolean): whether this expires as soon as it's paid out (always *true*) - **single_use** (boolean): whether this expires as soon as it's paid out (always *true*)
- **bolt12** (string): the bolt12 encoding of the offer - **bolt12** (string): the bolt12 encoding of the offer
- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without a signature
- **used** (boolean): True if an incoming invoice has been paid (always *false*) - **used** (boolean): True if an incoming invoice has been paid (always *false*)
- **label** (string, optional): the (optional) user-specified label - **label** (string, optional): the (optional) user-specified label
[comment]: # (GENERATE-FROM-SCHEMA-END) [comment]: # (GENERATE-FROM-SCHEMA-END)
@@ -92,4 +97,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning> Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:6db3fdba07f376e697326b3bf1bd74c013084a459cb9f4fe76d23fce58bd58fe) [comment]: # ( SHA256STAMP:2b7e7b543a88a10dbfbca2508e034af79f43ed0845abdb9df1fdf7e28ee33c26)

View File

@@ -28,7 +28,7 @@
} }
}, },
"then": { "then": {
"required": [ "offer_id", "node_id", "signature", "description" ], "required": [ "offer_id", "node_id", "description" ],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"type": { }, "type": { },

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"type": "object", "type": "object",
"required": [ "offer_id", "active", "single_use", "bolt12", "used" ], "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used" ],
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"offer_id": { "offer_id": {
@@ -23,6 +23,10 @@
"type": "string", "type": "string",
"description": "The bolt12 string representing this offer" "description": "The bolt12 string representing this offer"
}, },
"bolt12_unsigned": {
"type": "string",
"description": "The bolt12 string representing this offer, without signature"
},
"used": { "used": {
"type": "boolean", "type": "boolean",
"description": "Whether the offer has had an invoice paid / payment made" "description": "Whether the offer has had an invoice paid / payment made"

View File

@@ -9,7 +9,7 @@
"items": { "items": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "offer_id", "active", "single_use", "bolt12", "used" ], "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used" ],
"properties": { "properties": {
"offer_id": { "offer_id": {
"type": "hex", "type": "hex",
@@ -29,6 +29,10 @@
"type": "string", "type": "string",
"description": "the bolt12 encoding of the offer" "description": "the bolt12 encoding of the offer"
}, },
"bolt12_unsigned": {
"type": "string",
"description": "the bolt12 encoding of the offer, without signature"
},
"used": { "used": {
"type": "boolean", "type": "boolean",
"description": "True if an associated invoice has been paid" "description": "True if an associated invoice has been paid"

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "offer_id", "active", "single_use", "bolt12", "used" ], "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used" ],
"properties": { "properties": {
"offer_id": { "offer_id": {
"type": "hex", "type": "hex",
@@ -23,6 +23,10 @@
"type": "string", "type": "string",
"description": "the bolt12 encoding of the offer" "description": "the bolt12 encoding of the offer"
}, },
"bolt12_unsigned": {
"type": "string",
"description": "the bolt12 encoding of the offer, without a signature"
},
"used": { "used": {
"type": "boolean", "type": "boolean",
"enum": [ false ], "enum": [ false ],

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "offer_id", "active", "single_use", "bolt12", "used" ], "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used" ],
"properties": { "properties": {
"offer_id": { "offer_id": {
"type": "hex", "type": "hex",
@@ -24,6 +24,10 @@
"type": "string", "type": "string",
"description": "the bolt12 encoding of the offer" "description": "the bolt12 encoding of the offer"
}, },
"bolt12_unsigned": {
"type": "string",
"description": "the bolt12 encoding of the offer, without a signature"
},
"used": { "used": {
"type": "boolean", "type": "boolean",
"enum": [ false ], "enum": [ false ],

View File

@@ -14,6 +14,7 @@
static void json_populate_offer(struct json_stream *response, static void json_populate_offer(struct json_stream *response,
const struct sha256 *offer_id, const struct sha256 *offer_id,
const char *b12, const char *b12,
const char *b12_nosig,
const struct json_escape *label, const struct json_escape *label,
enum offer_status status) enum offer_status status)
{ {
@@ -21,6 +22,8 @@ static void json_populate_offer(struct json_stream *response,
json_add_bool(response, "active", offer_status_active(status)); json_add_bool(response, "active", offer_status_active(status));
json_add_bool(response, "single_use", offer_status_single(status)); json_add_bool(response, "single_use", offer_status_single(status));
json_add_string(response, "bolt12", b12); json_add_string(response, "bolt12", b12);
if (b12_nosig)
json_add_string(response, "bolt12_unsigned", b12_nosig);
json_add_bool(response, "used", offer_status_used(status)); json_add_bool(response, "used", offer_status_used(status));
if (label) if (label)
json_add_escaped_string(response, "label", label); json_add_escaped_string(response, "label", label);
@@ -33,7 +36,7 @@ static struct command_result *param_b12_offer(struct command *cmd,
struct tlv_offer **offer) struct tlv_offer **offer)
{ {
char *fail; char *fail;
*offer = offer_decode_nosig(cmd, buffer + tok->start, *offer = offer_decode(cmd, buffer + tok->start,
tok->end - tok->start, tok->end - tok->start,
cmd->ld->our_features, chainparams, &fail); cmd->ld->our_features, chainparams, &fail);
if (!*offer) if (!*offer)
@@ -83,7 +86,7 @@ static struct command_result *json_createoffer(struct command *cmd,
struct json_escape *label; struct json_escape *label;
struct tlv_offer *offer; struct tlv_offer *offer;
struct sha256 merkle; struct sha256 merkle;
const char *b12str; const char *b12str, *b12str_nosig;
bool *single_use; bool *single_use;
enum offer_status status; enum offer_status status;
struct pubkey32 key; struct pubkey32 key;
@@ -112,9 +115,11 @@ static struct command_result *json_createoffer(struct command *cmd,
OFFER_ALREADY_EXISTS, OFFER_ALREADY_EXISTS,
"Duplicate offer"); "Duplicate offer");
} }
offer->signature = tal_free(offer->signature);
b12str_nosig = offer_encode(cmd, offer);
response = json_stream_success(cmd); response = json_stream_success(cmd);
json_populate_offer(response, &merkle, b12str, label, status); json_populate_offer(response, &merkle, b12str, b12str_nosig, label, status);
return command_success(cmd, response); return command_success(cmd, response);
} }
@@ -126,6 +131,25 @@ static const struct json_command createoffer_command = {
}; };
AUTODATA(json_command, &createoffer_command); AUTODATA(json_command, &createoffer_command);
/* We store strings in the db, so removing signatures is easiest by conversion */
static const char *offer_str_nosig(const tal_t *ctx,
struct lightningd *ld,
const char *b12str)
{
char *fail;
struct tlv_offer *offer = offer_decode(tmpctx, b12str, strlen(b12str),
ld->our_features, chainparams,
&fail);
if (!offer) {
log_broken(ld->log, "Cannot reparse offerstr from db %s: %s",
b12str, fail);
return NULL;
}
offer->signature = tal_free(offer->signature);
return offer_encode(ctx, offer);
}
static struct command_result *json_listoffers(struct command *cmd, static struct command_result *json_listoffers(struct command *cmd,
const char *buffer, const char *buffer,
const jsmntok_t *obj UNNEEDED, const jsmntok_t *obj UNNEEDED,
@@ -153,7 +177,9 @@ static struct command_result *json_listoffers(struct command *cmd,
if (b12 && offer_status_active(status) >= *active_only) { if (b12 && offer_status_active(status) >= *active_only) {
json_object_start(response, NULL); json_object_start(response, NULL);
json_populate_offer(response, json_populate_offer(response,
offer_id, b12, label, status); offer_id, b12,
offer_str_nosig(tmpctx, cmd->ld, b12),
label, status);
json_object_end(response); json_object_end(response);
} }
} else { } else {
@@ -168,7 +194,10 @@ static struct command_result *json_listoffers(struct command *cmd,
if (offer_status_active(status) >= *active_only) { if (offer_status_active(status) >= *active_only) {
json_object_start(response, NULL); json_object_start(response, NULL);
json_populate_offer(response, json_populate_offer(response,
&id, b12, label, status); &id, b12,
offer_str_nosig(tmpctx,
cmd->ld, b12),
label, status);
json_object_end(response); json_object_end(response);
} }
} }
@@ -213,7 +242,10 @@ static struct command_result *json_disableoffer(struct command *cmd,
status = wallet_offer_disable(wallet, offer_id, status); status = wallet_offer_disable(wallet, offer_id, status);
response = json_stream_success(cmd); response = json_stream_success(cmd);
json_populate_offer(response, offer_id, b12, label, status); json_populate_offer(response, offer_id, b12,
offer_str_nosig(tmpctx,
cmd->ld, b12),
label, status);
return command_success(cmd, response); return command_success(cmd, response);
} }

View File

@@ -3823,6 +3823,7 @@ def test_offer(node_factory, bitcoind):
offer = only_one(l1.rpc.call('listoffers', [ret['offer_id']])['offers']) offer = only_one(l1.rpc.call('listoffers', [ret['offer_id']])['offers'])
assert offer['bolt12'] == ret['bolt12'] assert offer['bolt12'] == ret['bolt12']
assert offer['bolt12_unsigned'] == ret['bolt12_unsigned']
assert offer['offer_id'] == ret['offer_id'] assert offer['offer_id'] == ret['offer_id']
output = subprocess.check_output([bolt12tool, 'decode', output = subprocess.check_output([bolt12tool, 'decode',
@@ -3831,6 +3832,12 @@ def test_offer(node_factory, bitcoind):
assert 'amount' not in output assert 'amount' not in output
else: else:
assert 'amount' in output assert 'amount' in output
output = subprocess.check_output([bolt12tool, 'decode',
offer['bolt12_unsigned']]).decode('ASCII')
if amount == 'any':
assert 'amount' not in output
else:
assert 'amount' in output
# Try wrong amount precision: # Try wrong amount precision:
with pytest.raises(RpcError, match='Currency AUD requires 2 minor units'): with pytest.raises(RpcError, match='Currency AUD requires 2 minor units'):
@@ -3985,7 +3992,7 @@ def test_fetchinvoice(node_factory, bitcoind):
'description': 'simple test'}) 'description': 'simple test'})
inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']})
inv2 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) inv2 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12_unsigned']})
assert inv1 != inv2 assert inv1 != inv2
assert 'next_period' not in inv1 assert 'next_period' not in inv1
assert 'next_period' not in inv2 assert 'next_period' not in inv2
@@ -4244,9 +4251,8 @@ def test_sendinvoice(node_factory, bitcoind):
assert only_one(l1.rpc.call('listoffers', [offer['offer_id']])['offers'])['used'] is False assert only_one(l1.rpc.call('listoffers', [offer['offer_id']])['offers'])['used'] is False
# sendinvoice should work. # sendinvoice should work.
out = l2.rpc.call('sendinvoice', {'offer': offer['bolt12'], out = l2.rpc.call('sendinvoice', {'offer': offer['bolt12_unsigned'],
'label': 'test sendinvoice 1'}) 'label': 'test sendinvoice 1'})
print(out)
assert out['label'] == 'test sendinvoice 1' assert out['label'] == 'test sendinvoice 1'
assert out['description'] == 'simple test' assert out['description'] == 'simple test'
assert 'bolt12' in out assert 'bolt12' in out