diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json index c68e3830953d..940437d42ea8 100644 --- a/contrib/msggen/msggen/schema.json +++ b/contrib/msggen/msggen/schema.json @@ -2360,6 +2360,619 @@ } ] }, + "lightning-bkpr-editdescriptionbyoutpoint.json": { + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "bkpr-editdescriptionbyoutpoint", + "title": "Command to change the description for events with {outpoint} after they're made", + "description": [ + "The **bkpr-editdescriptionbyoutpoint** RPC command updates all chain and channel events that match the {outpoint} with the provided {description}" + ], + "request": { + "required": [ + "outpoint", + "description" + ], + "properties": { + "outpoint": { + "type": "string", + "description": [ + "The outpoint to update the description for." + ] + }, + "description": { + "type": "string", + "description": [ + "The description to update to" + ] + } + } + }, + "response": { + "required": [ + "updated" + ], + "properties": { + "updated": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "type", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp", + "description" + ], + "properties": { + "account": { + "type": "string", + "description": [ + "The account name. If the account is a channel, the channel_id." + ] + }, + "type": { + "type": "string", + "enum": [ + "chain", + "channel" + ], + "description": [ + "Coin movement type." + ] + }, + "tag": { + "type": "string", + "description": [ + "Description of movement." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited." + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited." + ] + }, + "currency": { + "type": "string", + "description": [ + "Human-readable bech32 part for this coin type." + ] + }, + "timestamp": { + "type": "u32", + "description": [ + "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp." + ] + }, + "description": { + "type": "string", + "description": [ + "The description of this event" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "chain" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": { + "type": "string", + "description": [ + "A description of this outpoint." + ] + }, + "outpoint": { + "type": "string", + "description": [ + "The txid:outnum for this event." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "For chain events, blockheight this occured at." + ] + }, + "origin": { + "type": "string", + "description": [ + "The account this movement originated from." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "outpoint", + "blockheight" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "onchain_fee" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "txid" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "channel" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "fees_msat": { + "type": "msat", + "description": [ + "Amount paid in fees." + ] + }, + "is_rebalance": { + "type": "boolean", + "description": [ + "Is this payment part of a rebalance." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "part_id": { + "type": "u32", + "description": [ + "Counter for multi-part payments." + ] + } + }, + "additionalProperties": false + } + } + ] + } + } + } + }, + "author": [ + "Lisa Neigut <> is mainly responsible." + ], + "see_also": [ + "lightning-bkpr-editdescriptionbypaymentid(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)" + ], + "resources": [ + "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:bkpr-editdescriptionbyoutpoint#1", + "method": "bkpr-editdescriptionbyoutpoint", + "params": { + "outpoint": "55edd73a01f08057e1f5f61788477c0d7551d5d1aa2f2907bf9d03b6370bbacf:1", + "description": "edited utxo description" + } + }, + "response": { + "updated": [ + { + "account": "wallet", + "type": "chain", + "tag": "deposit", + "credit_msat": 200000000000, + "debit_msat": 0, + "currency": "bcrt", + "outpoint": "55edd73a01f08057e1f5f61788477c0d7551d5d1aa2f2907bf9d03b6370bbacf:1", + "timestamp": 1724554221, + "blockheight": 105, + "description": "edited utxo description" + } + ] + } + }, + { + "request": { + "id": "example:bkpr-editdescriptionbyoutpoint#2", + "method": "bkpr-editdescriptionbyoutpoint", + "params": { + "outpoint": "6472b4c9d39d8478ed9c848df7a62a512d953a4b2e6e7b09902d76a7bbb761ca:1", + "description": "edited utxo description" + } + }, + "response": { + "updated": [] + } + } + ] + }, + "lightning-bkpr-editdescriptionbypaymentid.json": { + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "bkpr-editdescriptionbypaymentid", + "title": "Command to change the description for events with {payment_id} after they're made", + "description": [ + "The **bkpr-editdescriptionbypaymentid** RPC command updates all chain and channel events that match the {payment_id} with the provided {description}" + ], + "request": { + "required": [ + "payment_id", + "description" + ], + "properties": { + "payment_id": { + "type": "string", + "description": [ + "The payment hash (payment_id) to update the description for." + ] + }, + "description": { + "type": "string", + "description": [ + "The description to update to" + ] + } + } + }, + "response": { + "required": [ + "updated" + ], + "properties": { + "updated": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "type", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp", + "description" + ], + "properties": { + "account": { + "type": "string", + "description": [ + "The account name. If the account is a channel, the channel_id." + ] + }, + "type": { + "type": "string", + "enum": [ + "chain", + "channel" + ], + "description": [ + "Coin movement type." + ] + }, + "tag": { + "type": "string", + "description": [ + "Description of movement." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited." + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited." + ] + }, + "currency": { + "type": "string", + "description": [ + "Human-readable bech32 part for this coin type." + ] + }, + "timestamp": { + "type": "u32", + "description": [ + "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp." + ] + }, + "description": { + "type": "string", + "description": [ + "The description of this event" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "chain" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "outpoint": { + "type": "string", + "description": [ + "The txid:outnum for this event." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "For chain events, blockheight this occured at." + ] + }, + "origin": { + "type": "string", + "description": [ + "The account this movement originated from." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "outpoint", + "blockheight" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "onchain_fee" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "txid" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "channel" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "fees_msat": { + "type": "msat", + "description": [ + "Amount paid in fees." + ] + }, + "is_rebalance": { + "type": "boolean", + "description": [ + "Is this payment part of a rebalance." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "part_id": { + "type": "u32", + "description": [ + "Counter for multi-part payments." + ] + } + }, + "additionalProperties": false + } + } + ] + } + } + } + }, + "author": [ + "Lisa Neigut <> is mainly responsible." + ], + "see_also": [ + "lightning-bkpr-editdescriptionbyoutpoint(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)" + ], + "resources": [ + "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:bkpr-editdescriptionbypaymentid#1", + "method": "bkpr-editdescriptionbypaymentid", + "params": { + "payment_id": "6ff673c6ae3527726cf200274426db79f93054a3cbbc4b3787d1afc2d65565a7", + "description": "edited invoice description" + } + }, + "response": { + "updated": [ + { + "account": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "type": "channel", + "tag": "invoice", + "credit_msat": 500000000, + "debit_msat": 0, + "currency": "bcrt", + "payment_id": "6ff673c6ae3527726cf200274426db79f93054a3cbbc4b3787d1afc2d65565a7", + "part_id": 0, + "timestamp": 1724554257, + "description": "edited invoice description", + "is_rebalance": false + } + ] + } + }, + { + "request": { + "id": "example:bkpr-editdescriptionbypaymentid#2", + "method": "bkpr-editdescriptionbypaymentid", + "params": { + "payment_id": "c97b61113636256111835c0204d70111c42f19069cefdc659849a6afc6b595a4", + "description": "edited invoice description" + } + }, + "response": { + "updated": [] + } + } + ] + }, "lightning-bkpr-inspect.json": { "$schema": "../rpc-schema-draft.json", "type": "object", @@ -2750,6 +3363,12 @@ "debit_msat": {}, "currency": {}, "timestamp": {}, + "description": { + "type": "string", + "description": [ + "The description of this event." + ] + }, "outpoint": { "type": "string", "description": [ @@ -2779,12 +3398,6 @@ "description": [ "The txid of the transaction that created this event." ] - }, - "description": { - "type": "string", - "description": [ - "The description of this event." - ] } }, "required": [ diff --git a/doc/schemas/lightning-bkpr-editdescriptionbyoutpoint.json b/doc/schemas/lightning-bkpr-editdescriptionbyoutpoint.json new file mode 100644 index 000000000000..5c43dba547e0 --- /dev/null +++ b/doc/schemas/lightning-bkpr-editdescriptionbyoutpoint.json @@ -0,0 +1,309 @@ +{ + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "bkpr-editdescriptionbyoutpoint", + "title": "Command to change the description for events with {outpoint} after they're made", + "description": [ + "The **bkpr-editdescriptionbyoutpoint** RPC command updates all chain and channel events that match the {outpoint} with the provided {description}" + ], + "request": { + "required": [ + "outpoint", + "description" + ], + "properties": { + "outpoint": { + "type": "string", + "description": [ + "The outpoint to update the description for." + ] + }, + "description": { + "type": "string", + "description": [ + "The description to update to" + ] + } + } + }, + "response": { + "required": [ + "updated" + ], + "properties": { + "updated": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "type", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp", + "description" + ], + "properties": { + "account": { + "type": "string", + "description": [ + "The account name. If the account is a channel, the channel_id." + ] + }, + "type": { + "type": "string", + "enum": [ + "chain", + "channel" + ], + "description": [ + "Coin movement type." + ] + }, + "tag": { + "type": "string", + "description": [ + "Description of movement." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited." + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited." + ] + }, + "currency": { + "type": "string", + "description": [ + "Human-readable bech32 part for this coin type." + ] + }, + "timestamp": { + "type": "u32", + "description": [ + "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp." + ] + }, + "description": { + "type": "string", + "description": [ + "The description of this event" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "chain" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": { + "type": "string", + "description": [ + "A description of this outpoint." + ] + }, + "outpoint": { + "type": "string", + "description": [ + "The txid:outnum for this event." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "For chain events, blockheight this occured at." + ] + }, + "origin": { + "type": "string", + "description": [ + "The account this movement originated from." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "outpoint", + "blockheight" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "onchain_fee" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "txid" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "channel" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "fees_msat": { + "type": "msat", + "description": [ + "Amount paid in fees." + ] + }, + "is_rebalance": { + "type": "boolean", + "description": [ + "Is this payment part of a rebalance." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "part_id": { + "type": "u32", + "description": [ + "Counter for multi-part payments." + ] + } + }, + "additionalProperties": false + } + } + ] + } + } + } + }, + "author": [ + "Lisa Neigut <> is mainly responsible." + ], + "see_also": [ + "lightning-bkpr-editdescriptionbypaymentid(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)" + ], + "resources": [ + "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:bkpr-editdescriptionbyoutpoint#1", + "method": "bkpr-editdescriptionbyoutpoint", + "params": { + "outpoint": "55edd73a01f08057e1f5f61788477c0d7551d5d1aa2f2907bf9d03b6370bbacf:1", + "description": "edited utxo description" + } + }, + "response": { + "updated": [ + { + "account": "wallet", + "type": "chain", + "tag": "deposit", + "credit_msat": 200000000000, + "debit_msat": 0, + "currency": "bcrt", + "outpoint": "55edd73a01f08057e1f5f61788477c0d7551d5d1aa2f2907bf9d03b6370bbacf:1", + "timestamp": 1724554221, + "blockheight": 105, + "description": "edited utxo description" + } + ] + } + }, + { + "request": { + "id": "example:bkpr-editdescriptionbyoutpoint#2", + "method": "bkpr-editdescriptionbyoutpoint", + "params": { + "outpoint": "6472b4c9d39d8478ed9c848df7a62a512d953a4b2e6e7b09902d76a7bbb761ca:1", + "description": "edited utxo description" + } + }, + "response": { + "updated": [] + } + } + ] +} diff --git a/doc/schemas/lightning-bkpr-editdescriptionbypaymentid.json b/doc/schemas/lightning-bkpr-editdescriptionbypaymentid.json new file mode 100644 index 000000000000..500dee8d4dad --- /dev/null +++ b/doc/schemas/lightning-bkpr-editdescriptionbypaymentid.json @@ -0,0 +1,304 @@ +{ + "$schema": "../rpc-schema-draft.json", + "type": "object", + "additionalProperties": false, + "rpc": "bkpr-editdescriptionbypaymentid", + "title": "Command to change the description for events with {payment_id} after they're made", + "description": [ + "The **bkpr-editdescriptionbypaymentid** RPC command updates all chain and channel events that match the {payment_id} with the provided {description}" + ], + "request": { + "required": [ + "payment_id", + "description" + ], + "properties": { + "payment_id": { + "type": "string", + "description": [ + "The payment hash (payment_id) to update the description for." + ] + }, + "description": { + "type": "string", + "description": [ + "The description to update to" + ] + } + } + }, + "response": { + "required": [ + "updated" + ], + "properties": { + "updated": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "type", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp", + "description" + ], + "properties": { + "account": { + "type": "string", + "description": [ + "The account name. If the account is a channel, the channel_id." + ] + }, + "type": { + "type": "string", + "enum": [ + "chain", + "channel" + ], + "description": [ + "Coin movement type." + ] + }, + "tag": { + "type": "string", + "description": [ + "Description of movement." + ] + }, + "credit_msat": { + "type": "msat", + "description": [ + "Amount credited." + ] + }, + "debit_msat": { + "type": "msat", + "description": [ + "Amount debited." + ] + }, + "currency": { + "type": "string", + "description": [ + "Human-readable bech32 part for this coin type." + ] + }, + "timestamp": { + "type": "u32", + "description": [ + "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp." + ] + }, + "description": { + "type": "string", + "description": [ + "The description of this event" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "chain" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "outpoint": { + "type": "string", + "description": [ + "The txid:outnum for this event." + ] + }, + "blockheight": { + "type": "u32", + "description": [ + "For chain events, blockheight this occured at." + ] + }, + "origin": { + "type": "string", + "description": [ + "The account this movement originated from." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "outpoint", + "blockheight" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "onchain_fee" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "txid": { + "type": "txid", + "description": [ + "The txid of the transaction that created this event." + ] + } + }, + "required": [ + "txid" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "channel" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "description": {}, + "fees_msat": { + "type": "msat", + "description": [ + "Amount paid in fees." + ] + }, + "is_rebalance": { + "type": "boolean", + "description": [ + "Is this payment part of a rebalance." + ] + }, + "payment_id": { + "type": "hex", + "description": [ + "Lightning payment identifier. For an htlc, this will be the preimage." + ] + }, + "part_id": { + "type": "u32", + "description": [ + "Counter for multi-part payments." + ] + } + }, + "additionalProperties": false + } + } + ] + } + } + } + }, + "author": [ + "Lisa Neigut <> is mainly responsible." + ], + "see_also": [ + "lightning-bkpr-editdescriptionbyoutpoint(7)", + "lightning-bkpr-listaccountevents(7)", + "lightning-bkpr-listincome(7)" + ], + "resources": [ + "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:bkpr-editdescriptionbypaymentid#1", + "method": "bkpr-editdescriptionbypaymentid", + "params": { + "payment_id": "6ff673c6ae3527726cf200274426db79f93054a3cbbc4b3787d1afc2d65565a7", + "description": "edited invoice description" + } + }, + "response": { + "updated": [ + { + "account": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "type": "channel", + "tag": "invoice", + "credit_msat": 500000000, + "debit_msat": 0, + "currency": "bcrt", + "payment_id": "6ff673c6ae3527726cf200274426db79f93054a3cbbc4b3787d1afc2d65565a7", + "part_id": 0, + "timestamp": 1724554257, + "description": "edited invoice description", + "is_rebalance": false + } + ] + } + }, + { + "request": { + "id": "example:bkpr-editdescriptionbypaymentid#2", + "method": "bkpr-editdescriptionbypaymentid", + "params": { + "payment_id": "c97b61113636256111835c0204d70111c42f19069cefdc659849a6afc6b595a4", + "description": "edited invoice description" + } + }, + "response": { + "updated": [] + } + } + ] +} diff --git a/doc/schemas/lightning-bkpr-listaccountevents.json b/doc/schemas/lightning-bkpr-listaccountevents.json index ee4c45790ff5..f9aa96668228 100644 --- a/doc/schemas/lightning-bkpr-listaccountevents.json +++ b/doc/schemas/lightning-bkpr-listaccountevents.json @@ -122,6 +122,12 @@ "debit_msat": {}, "currency": {}, "timestamp": {}, + "description": { + "type": "string", + "description": [ + "The description of this event." + ] + }, "outpoint": { "type": "string", "description": [ @@ -151,12 +157,6 @@ "description": [ "The txid of the transaction that created this event." ] - }, - "description": { - "type": "string", - "description": [ - "The description of this event." - ] } }, "required": [ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 78af58c6e4f4..21bc4e988f8a 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -352,6 +352,64 @@ static struct command_result *json_inspect(struct command *cmd, return command_finished(cmd, res); } +static void json_add_events(struct json_stream *res, + struct channel_event **channel_events, + struct chain_event **chain_events, + struct onchain_fee **onchain_fees) +{ + for (size_t i = 0, j = 0, k = 0; + i < tal_count(chain_events) + || j < tal_count(channel_events) + || k < tal_count(onchain_fees); + /* Incrementing happens inside loop */) { + struct channel_event *chan; + struct chain_event *chain; + struct onchain_fee *fee; + u64 lowest = 0; + + if (i < tal_count(chain_events)) + chain = chain_events[i]; + else + chain = NULL; + if (j < tal_count(channel_events)) + chan = channel_events[j]; + else + chan = NULL; + if (k < tal_count(onchain_fees)) + fee = onchain_fees[k]; + else + fee = NULL; + + if (chain) + lowest = chain->timestamp; + + if (chan + && (lowest == 0 || lowest > chan->timestamp)) + lowest = chan->timestamp; + + if (fee + && (lowest == 0 || lowest > fee->timestamp)) + lowest = fee->timestamp; + + /* chain events first, then channel events, then fees. */ + if (chain && chain->timestamp == lowest) { + json_add_chain_event(res, chain); + i++; + continue; + } + + if (chan && chan->timestamp == lowest) { + json_add_channel_event(res, chan); + j++; + continue; + } + + /* Last thing left is the fee */ + json_add_onchain_fee(res, fee); + k++; + } +} + /* Find all the events for this account, ordered by timestamp */ static struct command_result *json_list_account_events(struct command *cmd, const char *buf, @@ -414,58 +472,80 @@ static struct command_result *json_list_account_events(struct command *cmd, res = jsonrpc_stream_success(cmd); json_array_start(res, "events"); - for (size_t i = 0, j = 0, k = 0; - i < tal_count(chain_events) - || j < tal_count(channel_events) - || k < tal_count(onchain_fees); - /* Incrementing happens inside loop */) { - struct channel_event *chan; - struct chain_event *chain; - struct onchain_fee *fee; - u64 lowest = 0; + json_add_events(res, channel_events, chain_events, onchain_fees); + json_array_end(res); + return command_finished(cmd, res); +} - if (i < tal_count(chain_events)) - chain = chain_events[i]; - else - chain = NULL; - if (j < tal_count(channel_events)) - chan = channel_events[j]; - else - chan = NULL; - if (k < tal_count(onchain_fees)) - fee = onchain_fees[k]; - else - fee = NULL; +static struct command_result *param_outpoint(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct bitcoin_outpoint **outp) +{ + *outp = tal(cmd, struct bitcoin_outpoint); + if (json_to_outpoint(buffer, tok, *outp)) + return NULL; + return command_fail_badparam(cmd, name, buffer, tok, + "should be a txid:outnum"); +} - if (chain) - lowest = chain->timestamp; +static struct command_result *json_edit_desc_utxo(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct bitcoin_outpoint *outpoint; + const char *new_desc; + struct chain_event **chain_events; - if (chan - && (lowest == 0 || lowest > chan->timestamp)) - lowest = chan->timestamp; + if (!param(cmd, buf, params, + p_req("outpoint", param_outpoint, &outpoint), + p_req("description", param_string, &new_desc), + NULL)) + return command_param_failed(); - if (fee - && (lowest == 0 || lowest > fee->timestamp)) - lowest = fee->timestamp; + db_begin_transaction(db); + edit_utxo_description(db, outpoint, new_desc); + chain_events = get_chain_events_by_outpoint(cmd, db, outpoint, true); + db_commit_transaction(db); - /* chain events first, then channel events, then fees. */ - if (chain && chain->timestamp == lowest) { - json_add_chain_event(res, chain); - i++; - continue; - } + res = jsonrpc_stream_success(cmd); + json_array_start(res, "updated"); + json_add_events(res, NULL, chain_events, NULL); + json_array_end(res); - if (chan && chan->timestamp == lowest) { - json_add_channel_event(res, chan); - j++; - continue; - } + return command_finished(cmd, res); +} - /* Last thing left is the fee */ - json_add_onchain_fee(res, fee); - k++; - } +static struct command_result *json_edit_desc_payment_id(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct sha256 *identifier; + const char *new_desc; + struct channel_event **channel_events; + struct chain_event **chain_events; + + if (!param(cmd, buf, params, + p_req("payment_id", param_sha256, &identifier), + p_req("description", param_string, &new_desc), + NULL)) + return command_param_failed(); + + db_begin_transaction(db); + add_payment_hash_desc(db, identifier, new_desc); + + chain_events = get_chain_events_by_id(cmd, db, identifier); + channel_events = get_channel_events_by_id(cmd, db, identifier); + db_commit_transaction(db); + + res = jsonrpc_stream_success(cmd); + json_array_start(res, "updated"); + json_add_events(res, channel_events, chain_events, NULL); json_array_end(res); + return command_finished(cmd, res); } @@ -1975,6 +2055,14 @@ static const struct plugin_command commands[] = { "bkpr-channelsapy", json_channel_apy }, + { + "bkpr-editdescriptionbypaymentid", + json_edit_desc_payment_id + }, + { + "bkpr-editdescriptionbyoutpoint", + json_edit_desc_utxo + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 3f7dbf6c77cd..363a401de588 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -654,6 +654,26 @@ void maybe_mark_account_onchain(struct db *db, struct account *acct) tal_free(ctx); } +void edit_utxo_description(struct db *db, + struct bitcoin_outpoint *outpoint, + const char *desc) +{ + struct db_stmt *stmt; + + /* Ok, now we update the account with this blockheight */ + stmt = db_prepare_v2(db, SQL("UPDATE chain_events SET" + " ev_desc = ?" + " WHERE" + " utxo_txid = ?" + " AND outnum = ?" + " AND credit > 0")); + db_bind_text(stmt, desc); + db_bind_txid(stmt, &outpoint->txid); + db_bind_int(stmt, outpoint->n); + + db_exec_prepared_v2(take(stmt)); +} + void add_payment_hash_desc(struct db *db, struct sha256 *payment_hash, const char *desc) @@ -723,6 +743,108 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, return e; } +struct chain_event **get_chain_events_by_outpoint(const tal_t *ctx, + struct db *db, + const struct bitcoin_outpoint *outpoint, + bool credits_only) +{ + struct db_stmt *stmt; + if (credits_only) + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.origin" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + ", e.ignored" + ", e.stealable" + ", e.ev_desc" + ", e.spliced" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.utxo_txid = ?" + " AND e.outnum = ?" + " AND credit > 0")); + else + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.origin" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + ", e.ignored" + ", e.stealable" + ", e.ev_desc" + ", e.spliced" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.utxo_txid = ?" + " AND e.outnum = ?")); + + db_bind_txid(stmt, &outpoint->txid); + db_bind_int(stmt, outpoint->n); + return find_chain_events(ctx, take(stmt)); +} + +struct chain_event **get_chain_events_by_id(const tal_t *ctx, + struct db *db, + const struct sha256 *id) +{ + struct db_stmt *stmt; + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.origin" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + ", e.ignored" + ", e.stealable" + ", e.ev_desc" + ", e.spliced" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.payment_id = ?")); + + db_bind_sha256(stmt, id); + return find_chain_events(ctx, take(stmt)); +} + static struct chain_event *find_chain_event(const tal_t *ctx, struct db *db, const struct account *acct, diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 72e68f7d3c57..4be23df48afc 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -107,6 +107,17 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, u64 start_time, u64 end_time); +/* Get all chain events for a payment hash */ +struct chain_event **get_chain_events_by_id(const tal_t *ctx, + struct db *db, + const struct sha256 *id); + +/* Get all chain events for a utxo */ +struct chain_event **get_chain_events_by_outpoint(const tal_t *ctx, + struct db *db, + const struct bitcoin_outpoint *outpoint, + bool credits_only); + /* Calculate the balances for an account * * @calc_sum - compute the total balance. error if negative @@ -215,6 +226,12 @@ void add_payment_hash_desc(struct db *db, struct sha256 *payment_hash, const char *desc); +/* Set the description for all events on this outpoint to + * the provided one */ +void edit_utxo_description(struct db *db, + struct bitcoin_outpoint *outpoint, + const char *desc); + /* When we make external deposits from the wallet, we don't * count them until any output that was spent *into* them is * confirmed onchain. diff --git a/tests/autogenerate-rpc-examples.py b/tests/autogenerate-rpc-examples.py index 8317421bb82e..77b3315954b1 100644 --- a/tests/autogenerate-rpc-examples.py +++ b/tests/autogenerate-rpc-examples.py @@ -483,7 +483,18 @@ def generate_bookkeeper_examples(l2, l3, c23_chan_id): update_example(node=l3, method='bkpr-listaccountevents', params={}) update_example(node=l3, method='bkpr-listaccountevents', params=[c23_chan_id]) update_example(node=l3, method='bkpr-listincome', params={}) - update_example(node=l3, method='bkpr-listincome', params={'consolidate_fees': False}) + + # listincome and editing descriptions + listincome_result = update_example(node=l3, method='bkpr-listincome', params={'consolidate_fees': False}) + invoice = next((event for event in listincome_result['income_events'] if 'payment_id' in event), None) + utxo_event = next((event for event in listincome_result['income_events'] if 'outpoint' in event), None) + update_example(node=l3, method='bkpr-editdescriptionbypaymentid', params={'payment_id': invoice['payment_id'], 'description': 'edited invoice description'}) + # Try to edit a payment_id that does not exist + update_example(node=l3, method='bkpr-editdescriptionbypaymentid', params={'payment_id': 'c97b61113636256111835c0204d70111c42f19069cefdc659849a6afc6b595a4', 'description': 'edited invoice description'}) + update_example(node=l3, method='bkpr-editdescriptionbyoutpoint', params={'outpoint': utxo_event['outpoint'], 'description': 'edited utxo description'}) + # Try to edit an outpoint that does not exist + update_example(node=l3, method='bkpr-editdescriptionbyoutpoint', params={'outpoint': '6472b4c9d39d8478ed9c848df7a62a512d953a4b2e6e7b09902d76a7bbb761ca:1', 'description': 'edited utxo description'}) + logger.info('Bookkeeper Done!') except TaskFinished: raise diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index c32a59f6094b..ceadb883cea2 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -795,6 +795,31 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): assert l2_koinly_csv.find(bolt11_exp) >= 0 assert l2_koinly_csv.find(bolt12_exp) >= 0 + # Test that we can update the description, payment id + edited_desc_payid = 'edited payment_id description' + for node in [l1, l2]: + results = node.rpc.bkpr_editdescriptionbypaymentid(paid['payment_hash'], edited_desc_payid) + assert only_one(results['updated'])['description'] == edited_desc_payid + + # Test that we can update the description, outpoint + edited_desc_outpoint = 'edited outpoint description' + deposits = [ev for ev in l1_inc_ev if ev['tag'] == 'deposit'] + assert len(deposits) > 0 + results = l1.rpc.bkpr_editdescriptionbyoutpoint(deposits[0]['outpoint'], edited_desc_outpoint) + assert only_one(results['updated'])['description'] == edited_desc_outpoint + + # Test that input that doesn't match an event returns empty list + fake_outpoint = '01' * 32 + ':100' + results = l1.rpc.bkpr_editdescriptionbyoutpoint(fake_outpoint, edited_desc_outpoint) + assert len(results['updated']) == 0 + + # Make sure that only one event actually updated + acct_evs = l1.rpc.bkpr_listaccountevents()['events'] + income_evs = l1.rpc.bkpr_listincome()['income_events'] + for evs in [acct_evs, income_evs]: + assert only_one([ev for ev in evs if 'description' in ev and ev['description'] == edited_desc_payid]) + assert only_one([ev for ev in evs if 'description' in ev and ev['description'] == edited_desc_outpoint]) + def test_empty_node(node_factory, bitcoind): """ diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 591b4ebb3f30..95fa36e5898e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -4054,6 +4054,8 @@ def test_sql(node_factory, bitcoind): 'type': 'string'}, {'name': 'timestamp', 'type': 'u32'}, + {'name': 'description', + 'type': 'string'}, {'name': 'outpoint', 'type': 'string'}, {'name': 'blockheight', @@ -4064,8 +4066,6 @@ def test_sql(node_factory, bitcoind): 'type': 'hex'}, {'name': 'txid', 'type': 'txid'}, - {'name': 'description', - 'type': 'string'}, {'name': 'fees_msat', 'type': 'msat'}, {'name': 'is_rebalance',