bolt12: import latest spec (timestamp -> created_at).

@shesek points out that we called this field created_at in bolt11 decode,
which makes more sense anyway.

Changelog-EXPERIMENTAL: bolt12 decode `timestamp` field deprecated in favor of new name `created_at`.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-07-21 11:01:39 +09:30 committed by neil saitug
parent 90c5f9a051
commit 7769903f1a
13 changed files with 82 additions and 75 deletions

View file

@ -12,10 +12,10 @@ struct feature_set;
/* BOLT-offers #12:
* - if `relative_expiry` is present:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `timestamp` plus `seconds_from_timestamp`.
* is greater than `created_at` plus `seconds_from_creation`.
* - otherwise:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `timestamp` plus 7200.
* is greater than `created_at` plus 7200.
*/
#define BOLT12_DEFAULT_REL_EXPIRY 7200

View file

@ -379,9 +379,9 @@ static void print_payer_note(const char *payer_note)
(int)tal_bytelen(payer_note), payer_note);
}
static void print_timestamp(u64 timestamp)
static void print_created_at(u64 timestamp)
{
printf("timestamp: %"PRIu64" (%s)\n",
printf("created_at: %"PRIu64" (%s)\n",
timestamp, fmt_time(tmpctx, timestamp));
}
@ -396,27 +396,27 @@ static void print_cltv(u32 cltv)
printf("min_final_cltv_expiry: %u\n", cltv);
}
static void print_relative_expiry(u64 *timestamp, u32 *relative)
static void print_relative_expiry(u64 *created_at, u32 *relative)
{
/* Ignore if already malformed */
if (!timestamp)
if (!created_at)
return;
/* BOLT-offers #12:
* - if `relative_expiry` is present:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `timestamp` plus `seconds_from_timestamp`.
* is greater than `created_at` plus `seconds_from_creation`.
* - otherwise:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `timestamp` plus 7200.
* is greater than `created_at` plus 7200.
*/
if (!relative)
printf("relative_expiry: %u (%s) (default)\n",
BOLT12_DEFAULT_REL_EXPIRY,
fmt_time(tmpctx, *timestamp + BOLT12_DEFAULT_REL_EXPIRY));
fmt_time(tmpctx, *created_at + BOLT12_DEFAULT_REL_EXPIRY));
else
printf("relative_expiry: %u (%s)\n", *relative,
fmt_time(tmpctx, *timestamp + *relative));
fmt_time(tmpctx, *created_at + *relative));
}
static void print_fallbacks(const struct tlv_invoice_fallbacks *fallbacks)
@ -622,11 +622,11 @@ int main(int argc, char *argv[])
}
if (must_have(invoice, payer_key))
print_payer_key(invoice->payer_key, invoice->payer_info);
if (must_have(invoice, timestamp))
print_timestamp(*invoice->timestamp);
if (must_have(invoice, created_at))
print_created_at(*invoice->created_at);
if (invoice->payer_note)
print_payer_note(invoice->payer_note);
print_relative_expiry(invoice->timestamp,
print_relative_expiry(invoice->created_at,
invoice->relative_expiry);
if (must_have(invoice, payment_hash))
print_payment_hash(invoice->payment_hash);

View file

@ -153,11 +153,11 @@ If \fBtype\fR is "bolt12 invoice", and \fBvalid\fR is \fItrue\fR:
.IP \[bu]
\fBdescription\fR (string): the description of the purpose of the offer
.IP \[bu]
\fBtimestamp\fR (u64): the UNIX timestamp of the invoice
\fBcreated_at\fR (u64): the UNIX timestamp of invoice creation
.IP \[bu]
\fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR (always 64 characters)
.IP \[bu]
\fBrelative_expiry\fR (u32): the number of seconds after \fItimestamp\fR when this expires
\fBrelative_expiry\fR (u32): the number of seconds after \fIcreated_at\fR when this expires
.IP \[bu]
\fBmin_final_cltv_expiry\fR (u32): the number of blocks required by destination
.IP \[bu]
@ -256,7 +256,7 @@ the following warnings are possible:
.IP \[bu]
\fBwarning_invoice_missing_recurrence_basetime\fR: Has \fBrecurrence_counter\fR without \fBrecurrence_basetime\fR
.IP \[bu]
\fBwarning_invoice_missing_timestamp\fR: Missing \fBtimestamp\fR
\fBwarning_invoice_missing_created_at\fR: Missing \fBcreated_at\fR
.IP \[bu]
\fBwarning_invoice_missing_payment_hash\fR: Missing \fBpayment_hash\fR
.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
\" SHA256STAMP:cd54af7c631f06b3db72848cdf90951ceb14d89b8bca981dba69244cd2ddbae5
\" SHA256STAMP:dda6e1cff3e58c38b637c5a11673b5a9f4837cbdf89ed3be52a8bfc874af87d4

View file

@ -70,9 +70,9 @@ If **type** is "bolt12 invoice", and **valid** is *true*:
- **signature** (bip340sig): BIP-340 signature of the *node_id* on this offer
- **amount_msat** (msat): the amount in bitcoin
- **description** (string): the description of the purpose of the offer
- **timestamp** (u64): the UNIX timestamp of the invoice
- **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 *timestamp* when this expires
- **relative_expiry** (u32): the number of seconds after *created_at* when this expires
- **min_final_cltv_expiry** (u32): the number of blocks required by destination
- **offer_id** (hex, optional): the id of this offer (merkle hash of non-signature fields) (always 64 characters)
- **chains** (array of hexs, optional): which blockchains this offer is for (missing implies bitcoin mainnet only):
@ -108,7 +108,7 @@ If **type** is "bolt12 invoice", and **valid** is *false*:
- **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_timestamp**: Missing **timestamp**
- **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
@ -183,4 +183,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:8ca0b9178b8ea6575cd80291001263dc27f721664648086a7c1a02efcb545ee7)
[comment]: # ( SHA256STAMP:fbbfc116c23489d4f346b4d5eb06dd922592c543ec68e5a76b6822b2658bd46d)

View file

@ -263,7 +263,7 @@
}
},
"then": {
"required": [ "node_id", "signature", "amount_msat", "description", "timestamp", "payment_hash", "relative_expiry", "min_final_cltv_expiry" ],
"required": [ "node_id", "signature", "amount_msat", "description", "created_at", "payment_hash", "relative_expiry", "min_final_cltv_expiry" ],
"additionalProperties": false,
"properties": {
"type": { },
@ -378,8 +378,11 @@
"description": "the payer-provided blob to derive payer_key"
},
"timestamp": {
"deprecated": true
},
"created_at": {
"type": "u64",
"description": "the UNIX timestamp of the invoice"
"description": "the UNIX timestamp of invoice creation"
},
"payment_hash": {
"type": "hex",
@ -389,7 +392,7 @@
},
"relative_expiry": {
"type": "u32",
"description": "the number of seconds after *timestamp* when this expires"
"description": "the number of seconds after *created_at* when this expires"
},
"min_final_cltv_expiry": {
"type": "u32",
@ -462,6 +465,7 @@
"payer_key": { },
"payer_info": { },
"timestamp": { },
"created_at": { },
"payment_hash": { },
"relative_expiry": { },
"min_final_cltv_expiry": { },
@ -487,9 +491,9 @@
"type": "string",
"description": "Has **recurrence_counter** without **recurrence_basetime**"
},
"warning_invoice_missing_timestamp": {
"warning_invoice_missing_created_at": {
"type": "string",
"description": "Missing **timestamp**"
"description": "Missing **created_at**"
},
"warning_invoice_missing_payment_hash": {
"type": "string",

View file

@ -1413,17 +1413,17 @@ static struct command_result *json_sendinvoice(struct command *cmd,
}
/* BOLT-offers #12:
* - MUST set `timestamp` to the number of seconds since Midnight 1
* January 1970, UTC.
* - MUST set `created_at` to the number of seconds since Midnight 1
* January 1970, UTC when the offer was created.
*/
sent->inv->timestamp = tal(sent->inv, u64);
*sent->inv->timestamp = time_now().ts.tv_sec;
sent->inv->created_at = tal(sent->inv, u64);
*sent->inv->created_at = time_now().ts.tv_sec;
/* BOLT-offers #12:
* - if the expiry for accepting payment is not 7200 seconds after
* `timestamp`:
* - MUST set `relative_expiry` `seconds_from_timestamp` to the number
* of seconds after `timestamp` that payment of this invoice should
* `created_at`:
* - MUST set `relative_expiry` `seconds_from_creation` to the number
* of seconds after `created_at` that payment of this invoice should
* not be attempted.
*/
if (sent->wait_timeout != 7200) {

View file

@ -543,13 +543,16 @@ static void json_add_b12_invoice(struct json_stream *js,
tal_bytelen(invoice->payer_note));
/* BOLT-offers #12:
* - MUST reject the invoice if `timestamp` is not present.
* - MUST reject the invoice if `created_at` is not present.
*/
if (invoice->timestamp)
json_add_u64(js, "timestamp", *invoice->timestamp);
else {
json_add_string(js, "warning_invoice_missing_timestamp",
"invoices without a timestamp are invalid");
if (invoice->created_at) {
/* FIXME: Remove soon! */
if (deprecated_apis)
json_add_u64(js, "timestamp", *invoice->created_at);
json_add_u64(js, "created_at", *invoice->created_at);
} else {
json_add_string(js, "warning_invoice_missing_created_at",
"invoices without created_at are invalid");
valid = false;
}
@ -567,7 +570,7 @@ static void json_add_b12_invoice(struct json_stream *js,
/* BOLT-offers #12:
*
* - if the expiry for accepting payment is not 7200 seconds after
* `timestamp`:
* `created_at`:
* - MUST set `relative_expiry`
*/
if (invoice->relative_expiry)

View file

@ -114,8 +114,8 @@ test_field(struct command *cmd,
* - if the invoice corresponds to an offer with `recurrence`:
* ...
* - if it sets `relative_expiry`:
* - MUST NOT set `relative_expiry` `seconds_from_timestamp` more than the
* number of seconds after `timestamp` that payment for this period will
* - MUST NOT set `relative_expiry` `seconds_from_creation` more than the
* number of seconds after `created_at` that payment for this period will
* be accepted.
*/
static void set_recurring_inv_expiry(struct tlv_invoice *inv, u64 last_pay)
@ -123,10 +123,10 @@ static void set_recurring_inv_expiry(struct tlv_invoice *inv, u64 last_pay)
inv->relative_expiry = tal(inv, u32);
/* Don't give them a 0 second invoice, even if it's true. */
if (last_pay <= *inv->timestamp)
if (last_pay <= *inv->created_at)
*inv->relative_expiry = 1;
else
*inv->relative_expiry = last_pay - *inv->timestamp;
*inv->relative_expiry = last_pay - *inv->created_at;
/* FIXME: Shorten expiry if we're doing currency conversion! */
}
@ -292,14 +292,14 @@ static struct command_result *check_period(struct command *cmd,
ir->offer->recurrence_base,
basetime, period_idx,
&paywindow_start, &paywindow_end);
if (*ir->inv->timestamp < paywindow_start) {
if (*ir->inv->created_at < paywindow_start) {
return fail_invreq(cmd, ir,
"period_index %"PRIu64
" too early (start %"PRIu64")",
period_idx,
paywindow_start);
}
if (*ir->inv->timestamp > paywindow_end) {
if (*ir->inv->created_at > paywindow_end) {
return fail_invreq(cmd, ir,
"period_index %"PRIu64
" too late (ended %"PRIu64")",
@ -327,9 +327,9 @@ static struct command_result *check_period(struct command *cmd,
u64 end = offer_period_start(basetime, period_idx + 1,
ir->offer->recurrence);
if (*ir->inv->timestamp > start) {
if (*ir->inv->created_at > start) {
*ir->inv->amount
*= (double)((*ir->inv->timestamp - start)
*= (double)((*ir->inv->created_at - start)
/ (end - start));
/* Round up to make it non-zero if necessary. */
if (*ir->inv->amount == 0)
@ -401,7 +401,7 @@ static struct command_result *check_previous_invoice(struct command *cmd,
/* No previous? Just pass through */
if (*ir->invreq->recurrence_counter == 0)
return check_period(cmd, ir, *ir->inv->timestamp);
return check_period(cmd, ir, *ir->inv->created_at);
req = jsonrpc_request_start(cmd->plugin, cmd,
"listinvoices",
@ -806,8 +806,8 @@ static struct command_result *listoffers_done(struct command *cmd,
ir->inv->cltv = tal_dup(ir->inv, u32, &cltv_final);
ir->inv->timestamp = tal(ir->inv, u64);
*ir->inv->timestamp = time_now().ts.tv_sec;
ir->inv->created_at = tal(ir->inv, u64);
*ir->inv->created_at = time_now().ts.tv_sec;
/* We may require currency lookup; if so, do it now. */
if (ir->offer->amount && ir->offer->currency)

View file

@ -2072,9 +2072,9 @@ static struct command_result *json_paymod(struct command *cmd,
if (!b12->payment_hash)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing payment_hash");
if (!b12->timestamp)
if (!b12->created_at)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing timestamp");
"invoice missing created_at");
if (b12->amount) {
invmsat = tal(cmd, struct amount_msat);
*invmsat = amount_msat(*b12->amount);
@ -2110,17 +2110,17 @@ static struct command_result *json_paymod(struct command *cmd,
/* BOLT-offers #12:
* - if `relative_expiry` is present:
* - MUST reject the invoice if the current time since
* 1970-01-01 UTC is greater than `timestamp` plus
* `seconds_from_timestamp`.
* 1970-01-01 UTC is greater than `created_at` plus
* `seconds_from_creation`.
* - otherwise:
* - MUST reject the invoice if the current time since
* 1970-01-01 UTC is greater than `timestamp` plus
* 1970-01-01 UTC is greater than `created_at` plus
* 7200.
*/
if (b12->relative_expiry)
invexpiry = *b12->timestamp + *b12->relative_expiry;
invexpiry = *b12->created_at + *b12->relative_expiry;
else
invexpiry = *b12->timestamp + BOLT12_DEFAULT_REL_EXPIRY;
invexpiry = *b12->created_at + BOLT12_DEFAULT_REL_EXPIRY;
p->local_offer_id = tal_steal(p, local_offer_id);
}

View file

@ -102,12 +102,12 @@ tlvtype,invoice,payer_note,39
tlvdata,invoice,payer_note,note,utf8,...
tlvtype,invoice,payer_info,50
tlvdata,invoice,payer_info,blob,byte,...
tlvtype,invoice,timestamp,40
tlvdata,invoice,timestamp,timestamp,tu64,
tlvtype,invoice,created_at,40
tlvdata,invoice,created_at,timestamp,tu64,
tlvtype,invoice,payment_hash,42
tlvdata,invoice,payment_hash,payment_hash,sha256,
tlvtype,invoice,relative_expiry,44
tlvdata,invoice,relative_expiry,seconds_from_timestamp,tu32,
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,cltv,46
tlvdata,invoice,cltv,min_final_cltv_expiry,tu32,
tlvtype,invoice,fallbacks,48

1 tlvtype,offer,chains,2
102 tlvdata,invoice,payer_note,note,utf8,...
103 tlvtype,invoice,payer_info,50
104 tlvdata,invoice,payer_info,blob,byte,...
105 tlvtype,invoice,timestamp,40 tlvtype,invoice,created_at,40
106 tlvdata,invoice,timestamp,timestamp,tu64, tlvdata,invoice,created_at,timestamp,tu64,
107 tlvtype,invoice,payment_hash,42
108 tlvdata,invoice,payment_hash,payment_hash,sha256,
109 tlvtype,invoice,relative_expiry,44
110 tlvdata,invoice,relative_expiry,seconds_from_timestamp,tu32, tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
111 tlvtype,invoice,cltv,46
112 tlvdata,invoice,cltv,min_final_cltv_expiry,tu32,
113 tlvtype,invoice,fallbacks,48

View file

@ -102,12 +102,12 @@ tlvtype,invoice,payer_note,39
tlvdata,invoice,payer_note,note,utf8,...
tlvtype,invoice,payer_info,50
tlvdata,invoice,payer_info,blob,byte,...
tlvtype,invoice,timestamp,40
tlvdata,invoice,timestamp,timestamp,tu64,
tlvtype,invoice,created_at,40
tlvdata,invoice,created_at,timestamp,tu64,
tlvtype,invoice,payment_hash,42
tlvdata,invoice,payment_hash,payment_hash,sha256,
tlvtype,invoice,relative_expiry,44
tlvdata,invoice,relative_expiry,seconds_from_timestamp,tu32,
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,cltv,46
tlvdata,invoice,cltv,min_final_cltv_expiry,tu32,
tlvtype,invoice,fallbacks,48

1 tlvtype,offer,chains,2
102 tlvdata,invoice,payer_note,note,utf8,...
103 tlvtype,invoice,payer_info,50
104 tlvdata,invoice,payer_info,blob,byte,...
105 tlvtype,invoice,timestamp,40 tlvtype,invoice,created_at,40
106 tlvdata,invoice,timestamp,timestamp,tu64, tlvdata,invoice,created_at,timestamp,tu64,
107 tlvtype,invoice,payment_hash,42
108 tlvdata,invoice,payment_hash,payment_hash,sha256,
109 tlvtype,invoice,relative_expiry,44
110 tlvdata,invoice,relative_expiry,seconds_from_timestamp,tu32, tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
111 tlvtype,invoice,cltv,46
112 tlvdata,invoice,cltv,min_final_cltv_expiry,tu32,
113 tlvtype,invoice,fallbacks,48

18
wire/bolt12_wiregen.c generated
View file

@ -1342,28 +1342,28 @@ static void fromwire_tlv_invoice_payer_info(const u8 **cursor, size_t *plen, voi
r->payer_info = *plen ? tal_arr(r, u8, *plen) : NULL;
fromwire_u8_array(cursor, plen, r->payer_info, *plen);
}
/* INVOICE MSG: timestamp */
static u8 *towire_tlv_invoice_timestamp(const tal_t *ctx, const void *vrecord)
/* INVOICE MSG: created_at */
static u8 *towire_tlv_invoice_created_at(const tal_t *ctx, const void *vrecord)
{
const struct tlv_invoice *r = vrecord;
u8 *ptr;
if (!r->timestamp)
if (!r->created_at)
return NULL;
ptr = tal_arr(ctx, u8, 0);
towire_tu64(&ptr, *r->timestamp);
towire_tu64(&ptr, *r->created_at);
return ptr;
}
static void fromwire_tlv_invoice_timestamp(const u8 **cursor, size_t *plen, void *vrecord)
static void fromwire_tlv_invoice_created_at(const u8 **cursor, size_t *plen, void *vrecord)
{
struct tlv_invoice *r = vrecord;
r->timestamp = tal(r, u64);
r->created_at = tal(r, u64);
*r->timestamp = fromwire_tu64(cursor, plen);
*r->created_at = fromwire_tu64(cursor, plen);
}
/* INVOICE MSG: payment_hash */
static u8 *towire_tlv_invoice_payment_hash(const tal_t *ctx, const void *vrecord)
@ -1553,7 +1553,7 @@ static const struct tlv_record_type tlvs_invoice[] = {
{ 36, towire_tlv_invoice_recurrence_counter, fromwire_tlv_invoice_recurrence_counter },
{ 38, towire_tlv_invoice_payer_key, fromwire_tlv_invoice_payer_key },
{ 39, towire_tlv_invoice_payer_note, fromwire_tlv_invoice_payer_note },
{ 40, towire_tlv_invoice_timestamp, fromwire_tlv_invoice_timestamp },
{ 40, towire_tlv_invoice_created_at, fromwire_tlv_invoice_created_at },
{ 42, towire_tlv_invoice_payment_hash, fromwire_tlv_invoice_payment_hash },
{ 44, towire_tlv_invoice_relative_expiry, fromwire_tlv_invoice_relative_expiry },
{ 46, towire_tlv_invoice_cltv, fromwire_tlv_invoice_cltv },
@ -1684,4 +1684,4 @@ bool invoice_error_is_valid(const struct tlv_invoice_error *record, size_t *err_
return tlv_fields_valid(record->fields, NULL, err_index);
}
// SHA256STAMP:27ffc38bc2be76e159508470734655f35e59d82927beb8c1f62917e592d76d10
// SHA256STAMP:2946a3a3734bd6af218cb73b4d42ec16fe68c1041b48e084cc160810870bcdd5

4
wire/bolt12_wiregen.h generated
View file

@ -120,7 +120,7 @@ struct tlv_invoice {
struct pubkey32 *payer_key;
utf8 *payer_note;
u8 *payer_info;
u64 *timestamp;
u64 *created_at;
struct sha256 *payment_hash;
u32 *relative_expiry;
u32 *cltv;
@ -323,4 +323,4 @@ struct fallback_address *fromwire_fallback_address(const tal_t *ctx, const u8 **
#endif /* LIGHTNING_WIRE_BOLT12_WIREGEN_H */
// SHA256STAMP:27ffc38bc2be76e159508470734655f35e59d82927beb8c1f62917e592d76d10
// SHA256STAMP:2946a3a3734bd6af218cb73b4d42ec16fe68c1041b48e084cc160810870bcdd5